![]() ![]() |
![]() |
|
![]() |
||
![]() |
2025-05-12
古いコードを捨てて1から書き直したからこそ続いているソフトウェア
Joel on SoftwareにNetScapeを例に、古いプログラムを捨てて1から書き直したくなるのは戦略ミスだって書いてあるけど、あのとき書き直してなかったら続いてないんではって思ったので、1から書き直して続いてるソフトウェアを挙げてみる。
Firefox
NetScapeからMozillaに移行するときに、新バージョンのリリースがなくなって、そこで致命的にシェアを落としたというのは確かにそうだと思う。
けど、そこで書き換えていなかったら、2005年のAJAXから始まるWebアプリの高度化についていけなかったと思う。
あそこで書き換えたからこそ、いまこの記事をFirefox上で書けてるんじゃなかろうか。
Windows
Windowsは、MS DOS上で動いていた3.1に継ぎ足すような形で32bit対応してWindows 95など続いていたけど、やはり無理が出ていて、ビジネス用にWindows NTを1から作り直していました。
そして一般向けにもNTを使おうと計画してたけど開発が遅れてWindows 98をアップデートしたWindows MEが出たりして、それでもWindows XPからはNTベースになって今も続いています。
macOS
MacのOSは、古いMac OSを捨ててmacOS(当時はMac OS X)に置き換えられました。開発は遅れて、いつまでもリリースされなかったのを覚えてます。当初、次期OSとしてはCoplandの開発が進んでいましたが、いつまでもリリースされなかったのはコレ。結局Coplandはキャンセルされて、NeXTを買収し、OPENSTEPをベースにMac OS Xが開発されたのでした。(その意味では1から開発されたわけではなかったので失敗例かもしれない)
それでUNIXベースになったからこそエンジニアの支持を得ているし、またそこでアーキテクチャが整理されたからiPhoneなどへの端末展開ができたんじゃないかと思います。
はてなブログ
いまこの記事が置かれてる はてなブログも古いコードを捨てて書き直されたサービスですね。
はてなダイアリーから はてなブログへの移行期に、おそらくなんらかのビジネスチャンスを失ったとは思うけど、それでも、あのときに刷新してなかったら、gooブログなどサービス終了するブログたちに名前を連ねてたかもしれない。
失敗例
業務システムは定期的に更新されてるけど、最近は失敗が目立つようになってますね。今までも失敗はあったけど、影響がでかくなってるような。
新システムに移行したらプリンが出荷できなくなったグリコ基幹システム。
グリコ純利益43%減 24年12月期決算、基幹システム障害で「プッチンプリン」など出荷停止 - 日本経済新聞
システム更新しようとしてたら隠れ仕様がたくさん出て来て訴訟問題になってるNHK受信料システム。
IBM Japan Newsroom - お知らせ
今、JavaはCで書かれているのだけど、JavaをJavaで書くというのは夢で、JavaバイトコードをネイティブコンパイルするコンパイラをJavaで書こうという話がありました。
で、Cで書かれた今のC2コンパイラのメンテが難しすぎるので置き換えようということでJavaで書かれたGraalコンパイラが開発されたのだけど、C2を置き換えるには至りませんでしたね。
とはいえ消えたわけじゃなく、GraalはGraalVMでネイティブコンパイルに使われていて、また標準化の流れも来ているけど。
JEP 317: Experimental Java-Based JIT Compiler
自社開発プロダクトは、移行失敗したら静かに消えていくので目立ちにくいんだろう。なんかあった気がするけど。
まとめ
Joel on Softwareには、移行のための3年というのはインターネットの世界では非常に長いと書かれていたけど、それから20年たって動きも落ち着いて、3年はそう長くないようにも見える。
AIに関わらないプロダクトだと、3年のギャップはそこまで問題にならないんでは。逆にAIに関するプロダクトは3ヵ月の遅れが致命的になってる。
ただ、見返してみると、FirefoxもWindowsもmacOSも、20世紀のコードを21世紀に入って捨てたもので、それ以降は安定して書き換えるという話になってないですね。
はてなブログも、2012年くらいに稼働してると思うけど、それ以降はコードベースが古いという話になっていないです。
企業システムも、メインフレームからオープンシステム、自前サーバーからクラウドのようなアーキテクチャ変更がモチベーションにあるように思います。
インターネットの世界も落ち着いて、16bitから32bit、クラウドやスマホのような実行基盤の変更も起きないように見えるので、今後は新しい実行環境で動かすために1から書き直すということは減っていきそう。
創業期につくったシステムが成長が一段落したときにユースケースが変わっているので作り変える、といったことはあるのかもしれないけど。
例えばPerlで書かれている はてなブログをPerl以外で書き直すということは なさそうで、新しい機能をいれるときにマイクロサービスとして切り出して別言語で実装というふうになると思う。既存部分を置き換えるにしても、同様にマイクロサービスとして切り出して置き換えていく形になりそう。
まあ、なんらか1から書き直す必然性は残るだろうけど、それは単にコードがダサいからとかではなく、今後の発展につながる形である必要があると思う。
Joel on Software
作者:ジョエル・スポルスキ
オーム社
Amazon
nowokay 2025-05-12 22:40 読者になる
もっと読む
コメントを書く
2025-05-11
CPUが得意なことをCPUにまかせて少ないVRAMでも大きめのLLMを速く動かす
Redditに「VRAM足りないとき一部のレイヤーをCPUに任せるんではなく、レイヤー全部をGPUに載せてレイヤー内部のFFNだけCPUに持っていったら速くなった、なんでこれが標準じゃないんだ」というのがあったので、おうちのRTX 4060 Ti 16GBで試してみたら微妙に速くなりました。
https://www.reddit.com/r/LocalLLaMA/comments/1ki7tg7/dont_offload_gguf_layers_offload_tensors_200_gen/
Qwen3 30B A3Bで試してみる
こういった指定がOllamaやLM Studioではできないので、今回はKoboldCPPというので試してます。
https://github.com/LostRuins/koboldcpp
KoboldCPPでは実用が厳しいので、llama.cppで試すほうがよさそう。
とりあえず、LM StudioでQwen3 30B A3Bのq3_k_xlを動かしたときは15.58tok/sec
48中38レイヤーをGPUに割り当てています。
ということで、koboldcppの実行。ダウンロードした実行ファイルに--overridetensorsと--modelと--gpulayersを指定して起動します。
koboldcpp.exe --overridetensors "blk\.([0-9]*[05])\.ffn_.*_exps\.=CPU" --model "D:\dev\gguf\unsloth\Qwen3-30B-A3B-GGUF\Qwen3-30B-A3B-UD-Q3_K_XL.gguf" --gpulayers 48
--overridetensors "blk\.([0-9]*[05])\.ffn_.*_exps\.=CPU"という指定が肝ですね。
0と5で終わるffn内の層がCPUに乗ります。
今回はRedditに書いてあった指定を使っているのだけど、層の名前を確認したいときは正規表現で.*を指定すれば全部CPUに乗るので確認できそう。
http://localhost:5001にアクセスして「bertとgptの違いは」と聞いてみます。
17.55tok/sec!12%速くなりましたね。
メモリ消費はこのくらい。
落としたときに2.2GB使っていたので、11.4GBほど消費してます。これはLM Studioで36レイヤー読み込んだときと同じ。
Llama4 ScoutのQ2_KをLM Studioで16レイヤーをGPUにオフロードした場合とKoboldCPPで--overridetensors "blk\.([0-9]*[0124578])\.ffn_.*_exps\.=CPU"としてFFNだけ2/3ほどCPUに残した場合では、4.1tok/secだったのが4.9tok/secと20%速くなりました。
ただ、思ったより効果がでてないのは、うちのCPUがちょっと弱いからではないかと。強いCPUならもっと効果が出ると思います。
Qwen3 32Bで試したら性能向上できなかったけど、CPUが強ければそれなりに効果が出そう。
何をやっているのか
では何をやっているのか見るためにLLMの基本構造を確認してみましょう。
いまのLLMはトランスフォーマという構造をベースにして、だいたいこんな感じになってる。位置エンコーディング(Posional Encoding)からFeed Forwardまでで一層で、 それがQwen 30B A3Bなら48層、Qwen 32Bなら64層という風になってる。
で、LM Studioをはじめ、LLMの実行系の設定では、層単位でGPUにどれだけ乗せるか、逆にCPUにどれだけ残すかというのを設定するようになってる。
でも、層全体で決めるんじゃなくて、層のなかの役割によってCPUでも効率化できるか、GPUじゃないとだめかって決まるんで、CPUでも効率化できるところはCPUに残して、GPUのメリットあるところはなるべくGPUに乗せたほうがいいんでは、って話ですね。
なぜそれがいいのか
じゃあなぜそれがいいのか、って見るのには、実際のコード見るのがいいと思います。
ということで、llama2.cをJavaで書き直したやつをベースに。
https://gist.github.com/kishida/05656bfcbe840f269784f7dbbee5928e
LLMの処理を見るのはforwardメソッド。
https://gist.github.com/kishida/05656bfcbe840f269784f7dbbee5928e#file-llama-java-L300
まず後段になるFeedForwardを見てみます。今回CPUに乗せようというのはこの部分です。
rmsnorm(s.xb, x, w.rms_ffn_weight[l], dim); // Now for FFN in PyTorch we have: self.w2(F.silu(self.w1(x)) * self.w3(x)) // first calculate self.w1(x) and self.w3(x) matmul(s.hb, s.xb, w.w1[l], dim, hidden_dim);
matmul(s.hb2, s.xb, w.w3[l], dim, hidden_dim); // SwiGLU non-linearity for (int i = 0; i < hidden_dim; i++) { // 省略 } // final matmul to get the output of the ffn matmul(s.xb, s.hb, w.w2[l], hidden_dim, dim);
SwiGLUのところは省略してますが1重ループです。rmsnormも1重ループになってます。1重ループは基本的に時間がかからないので、高速化の必要性も薄いです。
あとはmatmulです。FFNの処理時間はmatmul部分にかかります。
そのmatmulはこんな感じ。
static void matmul(float[] xout, float[] x, FloatBuffer ws, int n, int d) {
MemorySegment w = MemorySegment.ofBuffer(ws);
IntStream.range(0, d).parallel().forEach(i -> {
FloatVector val = FloatVector.zero(SPECIES); for (int j = 0; j < n; j+=SIMD_SIZE) {
FloatVector a = FloatVector.fromMemorySegment(
SPECIES, w, (i * n + j + SIMD_SIZE) * FLOAT_SIZE, ByteOrder.LITTLE_ENDIAN);
FloatVector b = FloatVector.fromArray(SPECIES, x, j + 0*SIMD_SIZE);
val = a.fma(b, val);
}
xout[i] = val.reduceLanes(VectorOperators.ADD);
});
}
細かいところは置いておいて、IntStreamでparallelとしてマルチスレッド化してるところと、その中にループがあってFloatVectorを使ってAVXなどSIMD命令を使うようにしてることだけ見てください。
つまり、スレッドを動かすコア数がそれなりにあってAVXのように1命令で複数のデータを処理できれば、CPUでも速く処理ができます。
一方でマルチヘッドアテンションはこんな感じですね。
// multihead attention. iterate over all heads final var fl = l;
IntStream.range(0, p.n_heads).parallel().forEach(h -> { int qpos = h * head_size; int kvpos = h / kv_mul * head_size; float[] att = s.att[h]; for (int t = 0; t <= pos; t++) { float score = 0;
FloatVector val = FloatVector.zero(SPECIES); for (int i = 0; i < head_size; i+=SIMD_SIZE) {
FloatVector a = FloatVector.fromArray(SPECIES, s.q, qpos + i);
FloatVector b = FloatVector.fromArray(SPECIES, s.key_cache[fl][t], kvpos + i);
val = a.fma(b, val);
}
score = val.reduceLanes(VectorOperators.ADD);
score /= head_aqrt; // save the score to the attention buffer att[t] = score;
}
・・・
IntStreamのparallelでマルチスレッド化して、内部にFloatVectorを使ったループがあるのはmatmulと似てるのだけど、FloatVectorを使ったループがループで囲まれて、全体で3重ループになってます。
そして、真ん中のループは特にハードウェアでの高速化がされてないです。CPUだとこれを高速化する仕組みがない。
Intel AMXとかあるけど4世代Xeonにようやく搭載されたところで、普及してない。使えるとLLMが速くなるようです。
インテルの AI 対応 AMX CPU アクセラレータのテスト結果について | Google Cloud 公式ブログ
一方でGPUだと3重ループを速くすることができます。
GPU処理の共通フレームワークであるOpenCLの説明に次のように書いてます。
解きたい問題には全て、直線状やキューブ状や平面状のようにある程度の次元性が存在している。 OpenCLでは最大3次元までを指定してカーネルを展開する。
ここで、サッと3重ループをGPUで効率よく処理したソースが出せるといいんだけど、ディープラーニングをGPU使って速くしようとした処理では、ちゃんと3重ループの処理が書けてなくて高速化できてなかった。
https://github.com/kishida/neuralnet/blob/use_jocl/src/main/resources/kernels/convolution_forward.cl#L15
次のようにiのループとjのループもGPUの並列化に任せるようにすると速くなるはず。
int fxy = get_global_id(0);
int i = get_global_id(1);
int j = get_global_id(2);
[増補改訂]GPUを支える技術 ――超並列ハードウェアの快進撃[技術基礎] (WEB+DB PRESS plus)
作者:Hisa Ando
技術評論社
Amazon
nowokay 2025-05-11 23:31 読者になる
もっと読む
コメントを書く
古いコードを捨てて1から書き直したからこそ続いているソフトウェア
CPUが得意なことをCPUにまかせて少ないVRAMでも大きめのLLMを速く動かす
ブログを開設した年のグループに入りましょう
はてなブログには、同じ話題でつながる「グループ」があります。まずはブログを開設した年のグループに入りましょう。同時期に始めたブログとつながることができます。
「2025年開設ブログ」のグループ
taediumの日記
読者になる
現場のためのソフトウェア開発プロセス - たかのり日記
読者になる
設計と実装の狭間で。
読者になる
▼ ▶
2013
2013 / 12
▼ ▶
2012
2012 / 12
▼ ▶
2011
2011 / 12
2011 / 8
2011 / 7
2011 / 6
2011 / 3
2011 / 1
▼ ▶
2010
2010 / 8
2010 / 7
2010 / 6
2010 / 5
2010 / 3
2010 / 2
▼ ▶
2009
2009 / 12
2009 / 9
2009 / 7
2009 / 6
2009 / 4
2009 / 3
2009 / 2
▼ ▶
2008
2008 / 12
2008 / 10
2008 / 8
2008 / 4
2008 / 3
2008 / 2
2008 / 1
▼ ▶
2007
2007 / 12
2007 / 11
2007 / 10
2007 / 7
2007 / 6
2007 / 5
2007 / 4
2007 / 3
2007 / 2
2007 / 1
▼ ▶
2006
2006 / 12
2006 / 11
2006 / 10
2006 / 9
2006 / 8
2006 / 7
2006 / 6
2006 / 5
2006 / 4
2006 / 3
2006 / 2
2006 / 1
▼ ▶
2005
2005 / 12
2005 / 11
2005 / 10
2005 / 9
2005 / 8
2005 / 7
2005 / 6
2005 / 5
2005 / 4
2005 / 3
2005 / 2
2005 / 1
▼ ▶
2004
2004 / 12
2004 / 11
2004 / 10
2004 / 9
2004 / 8
2004 / 7
月の生活 | よなよなエール公式ウェブサイト「よなよなの里」
読者になる
海外POSシステム開発エンジニア募集 by 株式会社Abby
現在、大規模チェーンレストラン向けのレストラン店舗運営システムを開発しています。展開先はアメリカ、オーストラリア、台湾、マレーシア、...
www.wantedly.com
Unityでスマホ、Switchのゲーム開発をしたいエンジニアを募集! by 株式会社Abby
今回は、・Unityを使用したNintendo Switch向けのゲーム開発・Unityを使用したスマートフォン向けのゲーム開発...
www.wantedly.com
2016-09-20
Seasar Conference Final
今週の土曜、9/24にSeasar Conference Finalを行います。
10年前に始まったSeasar Conferenceもいよいよ今度でFinalです。
お申し込みはこちら。
http://seasar.connpass.com/event/38679/
Seasar Projectの面白かったところって、開発方法論が盛り上がったところだと思うんですよ。
マーチンファウラーをはじめとして、著名な人たちのほとんどが「ドメインモデル推し」の中、僕は、「ステートレスなサービス + DTO推し」だったからね。S2Daoは、このために作ったようなものです。
あの開発方法論の議論に、かなりの人が参加したり、自分で考えたりしたでしょう。それが、面白かったところです。みんなが自分の事として考えたから。そんな難しい話ではないし、自分のプロジェクトに即いかせる話だしね。
オブジェクト指向ナンチャラとかDDDとか、素晴らしいものなんだけど、自分のプロジェクトで生かそうとするとそう簡単にはいかないからね。
Seasar2の開発が終了した事で、Seasar Projectに関わった多くの人たちの多くの時間が無駄にされたと、思っている人もいるようだけど、僕はそうは思わない。Seasar Projectに関わった事で、プログラマとしての能力や知名度をあげた人とって多い訳だから、それを否定する必要はないと思っています。
今度のSeasar Conferenceでは、「Seasar Projectのふりかえり」と「SXSW攻略法」の二つの話をします。
「Seasar Projectのふりかえり」では、昔話をしてもしょうがないので、いくつかのプロジェクトを「なぜ始めたのか」「うまくいったところとでその理由」「失敗したところとその理由」を話す事で、これから、新しくプロジェクトを立ち上げる人向けに、少しでも役に立つ話が出来ればと思います。
「SXSW攻略法」では、2016年のSXSWで、RealeaseItというアワードでファイナリストに残る事が出来たので、ファイナリストに残るためのこつ、そしてアワードをとれなかった理由。また、SXSWでは、ブースも出したので、ブースを出すときのポイントを話したいと思います。
Seasar Conference Finalお申し込みはこちらです。
http://seasar.connpass.com/event/38679/