Linuxカーネルのビルド時間を、LLMの支援で最大36%短縮するパッチが投稿されました。1
コンパイラの難しい最適化の話かと思ったのですが、パッチを読んでみると、もう少し身近な話も出てきます。
並び順を使わないのにソートする。同じオプションが使えるか何度も確認する。変更がないと判断するために、大量の情報を読み込む。
それぞれは地味です。でも、ビルドのたびに通る場所にある。
今回は、Lorenzo Stoakes氏によるビルド高速化パッチを追いながら、何を省き、どこで人間の判断が必要だったのかを見ていきます。
2026年9月14日投稿の v2・21本のパッチシリーズを中心に読んだ解説です。手元での追試記事ではありません。数値は投稿者の測定結果で、シリーズ全体の正式採用やリリース時期が確定したという意味ではありません。
何も変えていないのに、なぜ30秒かかるのか
最大36%という数字と並んで気になったのが、変更なしで再実行する no-opビルドです。
v2の測定では、EPYC環境のGCC・allmodconfigで、次の結果が報告されています。1
| ビルドの種類 | 変更前 | 変更後 | 時間の短縮率 |
|---|---|---|---|
| フルビルド | 188.0秒 | 121.1秒 | 約36% |
| 増分ビルド | 70.9秒 | 24.3秒 | 約66% |
| 変更なしの再実行 | 30.6秒 | 1.5秒 | 約95% |
何も変えていないのに30.6秒。コンパイルする仕事が少ないときほど、**「作り直す必要があるか調べる仕事」**の存在が目立ちます。
ここで、フルビルド、増分ビルド、no-opは分けて読む必要があります。全部作り直す時間と、少し直して確認する時間では、待っている処理が同じとは限りません。
毎日の開発で何度も繰り返すのは、後者かもしれません。大きなビルドを一度だけ速くする話として読むより、編集して確認するまでの待ち時間として見ると、印象が変わります。
上表は、2ソケットのEPYC 9754・合計256コア/512スレッドという環境での値です。全体のビルド時間は複数回測定したうちの最短値で、平均値ではありません。また、v2の性能値には、原文で指定された別の2つのパッチシリーズ、KBUILD_RUST_THREADS=8、pigzの導入という条件があります。「21本をどの環境に適用しても36%短縮する」とは読めません。1
make -jだけでは、ビルド全体を並列にできない
ビルドを速くするなら、まず並列数を増やしたくなります。
ただ、同時に実行できる仕事がなければ、CPUが余っていても使えません。前の処理の結果が必要な仕事は、その処理が終わるまで待つことになります。
例えば、次のような構造を考えます。説明用の模式例で、実際のカーネルビルドをそのまま描いたものではありません。
ファイルAのコンパイル ─┐
ファイルBのコンパイル ─┼─→ 結果をまとめる処理 → 後続処理
ファイルCのコンパイル ─┘
左側を何倍に並列化しても、真ん中の処理が1本でしか動かなければ、そこで待ちます。makeから複数のコマンドを起動できることと、起動したツールの内部まで並列化されていることは別です。
実際、v1への返信でStoakes氏は、リンク、modpost、objtoolが後続処理を止める区間を説明しています。2
今回のパッチを読むときも、「コンパイルをどれだけ速くしたか」だけでなく、コンパイルの前後で、何が順番待ちを作っていたのかを見ると追いやすくなります。
同じ仕事をやめるだけでも、待ち時間は減る
21本を順に並べると細部に埋もれてしまうので、ここでは処理の意図が分かりやすい3つを取り上げます。
並べ替えた結果を使わないなら、並べ替えない
nmは、オブジェクトファイルなどのシンボル情報を表示するツールです。パッチでは、既定の名前順ソートが不要な呼び出しに、ソートを省く-pを追加しています。3
説明用に単純化すると、違いはこれだけです。
# 既定の並べ替えを行う
nm vmlinux
# 並び順を利用しない処理へ渡す場合に、並べ替えを省く
nm -p vmlinux
もちろん、すべてのnmに付ければよいわけではありません。後続処理が順序に依存しない箇所を選ぶ必要があります。
この変更が面白いのは、高速なソートを実装したわけではないところです。そもそも、そのソートが必要なのかを見直しています。
パッチにはnm単体とビルド全体の測定が別々に載っています。単体の処理時間が減っても、別の処理と並行していれば、ビルド全体が同じ時間だけ短くなるとは限らないためです。3
自分の処理でも、データを整形した直後に必要な一部だけ取り出しているなら、その整形は本当に必要か、一度疑ってみたくなります。
コンパイラに、同じ質問を繰り返さない
別のパッチでは、makeの起動時に行うコンパイラ・リンカのオプション確認を減らしています。説明によると、この確認で約30回の実行が発生していました。4
「このフラグは使えるか」という確認自体は必要です。ただ、同じツール構成でビルドを続けるなら、何度も実行する必要があるかは別の話です。
そこで、適切な確認を、ビルド設定を管理するKconfig側のCC_HAS_*やLD_HAS_*へ移しています。
ただし、ここにも例外があります。Clangの-fno-stack-clash-protectionは対象アーキテクチャの条件に依存するため、Makefile側の確認を残しています。4
「結果を保存すれば速い」で終わらず、何が変わったら、その結果を使い回せなくなるのかまで考える必要があるわけです。
「変更なし」の確認で、大量の情報を読み直さない
depcheckというC製ツールを追加するパッチもあります。5
ビルドで使う.cmdファイルには、実行したコマンドやヘッダなどへの依存情報が入っています。パッチの説明では、1つのオブジェクトに1,000件を超える依存関係がある場合も挙げられています。
変更が少ないときにも、これを全部makeへ読ませると、確認するための処理が重くなります。
depcheckは依存先の更新時刻を先に確認し、同じ依存先へのstat(ファイル属性の取得)をまとめます。そのうえで、作り直しが不要な対象では依存情報を省いた断片を渡し、判断に必要な情報がある対象では元の内容を渡します。保存済みのコマンドは残すため、コマンド変更の確認まで捨てる設計ではありません。失敗時には従来の.cmd読み込みへ戻ります。5
何も変わっていないときほど、全部読む意味は薄い。でも、変わっていたら見落としてはいけない。
この両方を満たすために、確認をなくすのではなく、確認に必要な仕事を減らす構成になっています。
なお、この3つだけで最初の表の改善が出たわけではありません。表はシリーズ全体と記載条件での結果です。個別パッチの短縮率を足して、全体の短縮率にすることもできません。
LLMのコードは、そのまま採用されていない
ここまでだと、LLMに調査と実装を任せた成功例に見えます。
ただ、投稿者の説明には、気になる一文があります。6
It generated a lot of code, much of it hideous.
大量に生成されたコードの多くを、投稿者自身がかなり厳しく評価しています。
Stoakes氏は、LLMをボトルネック調査や改善案の検討だけでなく、ビルド、テスト、デバッグ、分析にも使った一方、コードを広く監査して書き直し、動作と性能も手動で確認したと説明しています。各コミットにはAssisted-byが付いています。6
ここは、生成されたコード量と、採用できるコード量を分けて考えたいところです。
仮説をたくさん試せることには価値があります。でも、その仮説が正しかったとしても、実装の複雑さや保守しやすさが自動的に解決するわけではありません。
また、公開された説明だけでは、人間だけで同じ調査をした場合より何時間短縮できたかまでは分かりません。「LLMを使った改善でビルドが速くなった」と、「LLMが人間より効率よく改善した」は、違う主張です。
速くなっても、既定で有効にしない変更がある
v2では、Rustコンパイラのフロントエンド並列化が、KBUILD_RUST_THREADSによる任意指定へ変更されています。理由は、出力の再現性です。1
ここでいう再現性は、「速度の測定をやり直せるか」ではありません。同じ入力から、同じビルド成果物を得られるかという話です。
速くなるなら全員に有効化すればよい、とはならない。
レビューでも、Kees Cook氏は改善を歓迎しつつ、一部の変更については増える複雑さと効果が釣り合うかを検討しています。7
これは高速化の記事として、数字と同じくらい大事な部分だと思います。
短くなった秒数だけなら比較しやすいですが、追加したキャッシュをいつ無効化するか、並列処理の不具合をどう調べるか、他の環境でも正しく動くかまで含めると、判断はもう少し難しくなります。
自分の開発で試すなら、「速くして」の前に分けて測る
今回のパッチをそのまま適用しなくても、調査の切り分け方は使えそうです。
まず、フルビルド、一部変更後、変更なしの再実行を別々に測る。そのうえで、CPUを使っている時間と、他の処理を待っている時間を分ける。
LLMへ頼むなら、最初から修正を求めるより、例えば次のような調査から始めたいです。
まだコードは変更しないでください。
ビルド処理を調べ、次の3つを分けて説明してください。
・実際のコンパイルに時間がかかっている場所
・コンパイル前後で、後続処理を待たせている場所
・変更がない場合にも繰り返している仕事
候補ごとに、根拠となる測定やコードの位置、
改善案が外れていると判断できる条件、
変更後も動作が同じであることを確かめる方法を挙げてください。
測定していないことは、推測として区別してください。
これは今回の投稿に載っていたプロンプトではなく、調査方法を参考に作った例です。
実際に比較するときは、ソースの版、設定、ツールチェーン、並列数、キャッシュ条件をそろえる。確認処理を省いて速くしたなら、入力や設定を変えたときには必要な処理が再実行されるかも見る。
「速くなった」と「仕事をしなくなった」は、時間だけでは区別できません。
おわりに
今回の話で面白かったのは、LLMが見慣れない最適化を発明した、という部分ではありません。
並び順が不要ならソートをしない。同じ質問を繰り返さない。変更がなければ読む量を減らす。個々の変更を読むと、考え方は意外と身近です。
一方で、それを長く使われるビルドシステムへ入れるには、例外や互換性まで見る必要があります。だから、生成したコードを大量に書き直したという説明にも納得できます。
自分の処理を速くしたくなったときも、難しいアルゴリズムを探す前に、「その仕事、本当に毎回必要なのか」から見直してみたいです。
参考資料
-
Lorenzo Stoakes, PATCH v2 00/21: kbuild: significantly speed up kernel builds, 2026-09-14。性能表、測定条件、v2変更履歴。原投稿のMessage-IDは
20260914-build-speedup-v2-0-39817ec5db23@kernel.org。 ↩ ↩2 ↩3 ↩4 -
v1の議論内の、Nick Desaulniers氏へのStoakes氏の返信。リンク・modpost・objtoolによる待ち時間の説明。 ↩
-
PATCH v2 04/21: kbuild: do not sort nm output where the order is irrelevant。 ↩ ↩2
-
PATCH v2 11/21: kbuild: avoid re-running compiler and linker probes。 ↩ ↩2
-
PATCH v2 09/21: kbuild: implement and use depcheck to check dependency timestamps。 ↩ ↩2