Linuxカーネルの開発を率いるLinus Torvalds氏が、AIコーディング支援ツール「Claude Code」を活用し、ファイル読み出し性能を改善する8本のパッチを公開した。10月10日付の説明によると、同じファイルを8スレッドで繰り返し読み出すテストでは、1秒当たりの処理回数が約3倍に増加した。今回の改善は、メモリにキャッシュされたデータを読み出す際に、複数のCPUが参照カウントを更新しようとして競合する問題に対処したものだ。ただし、公開されたのは開発ブランチ上の試作であり、読み出しを高速化する代わりにメモリの再利用を遅らせる仕組みには、なお解決すべき課題が残されている。
改善効果が大きいのは、同じファイルを並列に読み出す場合
Torvalds氏が10月10日の説明コミットで公開したベンチマークは、Core i7-8565Uを搭載したノートPCで実施された。CPUは4コア・8スレッド構成で、全コアがL3キャッシュを共有している。ファイルシステムにはext4を使用した。テストでは、各スレッドが別々のファイル記述子を使い、同じファイルの先頭から16バイトずつを pread() で繰り返し読み出している。対象ファイルのサイズは1ページ分だ。
比較対象となったのは、Linux 7.3-rc6に修正を加えたカーネルだ。CPUの脆弱性緩和策と、ユーザー空間へのデータコピーを追加検査する CONFIG_HARDENED_USERCOPY は無効化されている。つまり、一般的なLinuxディストリビューションをそのまま測定した結果ではない。また、測定に使用したPCは温度によってCPUの動作周波数が変化するため、Torvalds氏は1秒当たりの処理回数よりも、1回の読み出しに要するカーネル内のCPUサイクル数に注目するよう注意を促している。
| 16バイト読み出しの条件 | 変更前のサイクル数/回 | 変更後のサイクル数/回 |
|---|---|---|
| 1スレッド | 452〜454 | 408〜409 |
| 8スレッドが同じファイルを読む | 2,937〜2,975 | 725〜726 |
| 8スレッドがそれぞれ別のファイルを読む | 827 | 730〜732 |
同じファイルを8スレッドで読み出す場合、1回当たりのカーネル内のCPUサイクル数は約75.3〜75.6%減少した。一方、1スレッドでの削減率は約9.5〜10.1%にとどまっている。
これらの削減率は、Torvalds氏が公開した測定値をもとに、(1 − 変更後 ÷ 変更前)× 100 で算出したものだ。8スレッドの場合、下限は (1 − 726 ÷ 2937)× 100、上限は (1 − 725 ÷ 2975)× 100 とし、小数第1位まで求めている。1スレッドも同様に測定範囲の両端を組み合わせて計算しており、統計的な信頼区間を示すものではない。
同じファイルを8スレッドで読み出すテストでは、1秒当たりの処理回数も約1,000万回から3,000万回台へと増加した。しかし、スレッドごとに異なるファイルを読み出す場合、CPUサイクル数の差ははるかに小さい。
つまり、今回の変更が特に効果を発揮するのは、複数のCPUが同じデータの管理情報を更新しようとして競合する場面だ。ストレージの転送速度やLinux全体の処理性能が、一律に3倍になるわけではない。
データを読むだけなのに、なぜCPU間で競合が起きるのか
ファイルの内容は、読み出すたびにSSDなどのストレージから取得されるわけではない。Linuxは一度読み込んだデータを「ページキャッシュ」としてメモリに保持し、次回以降の読み出しに利用する。このメモリを管理する単位の一つが、複数のページをまとめて扱える folio だ。
従来の読み出し処理では、データをコピーしている途中にfolioが解放されないよう、読み出し前に参照カウントを増やし、コピーが終わると減らしていた。ファイルの内容を変更しなくても、読み出すたびに管理情報への書き込みが2回発生する。
複数のCPUが同じfolioを読み出す場合、それぞれが参照カウントを安全に更新する必要がある。そのため、参照カウントを含むキャッシュラインをCPU間で受け渡す処理が発生する。読み出すデータが小さい場合、実際のコピー処理よりも、共有する管理情報の更新に伴う待ち時間のほうが大きな負担になり得る。
Torvalds氏によると、この問題は2024年、Matthew Wilcox氏が顧客のワークロードをきっかけに提起していたという。
今回の8スレッドによる同一ファイルの読み出しテストでは、カーネル内で実行される命令数が1回当たり988〜989から864へ減少した。それに対して、CPUサイクル数は約4分の1まで減っている。単に実行する命令が少なくなっただけでなく、CPU間で共有する管理情報の更新負荷を減らしたことが、この大幅な性能改善につながったと考えられる。
RCUでメモリの再利用を遅らせ、参照カウントの更新を回避
今回の試作では、ページキャッシュから取り除いたメモリをすぐに別の用途へ再利用せず、読み出しが安全に完了するまで再利用を遅らせる方式を採用した。これにより、高速な読み出し経路ではfolioの参照カウントを増やすことなく、データをユーザー空間のバッファへ直接コピーできる。
この仕組みを支えるのが、RCU(Read-Copy-Update)と呼ばれる同期機構だ。Linux公式ドキュメントのRCU解説にあるように、RCUではデータ構造から対象を取り除く処理と、そのメモリを実際に解放する処理を分離する。対象が取り除かれる前から参照していた読み手がRCUの読み取り区間を終えるまで、メモリの再利用を待つ仕組みだ。
重要なのは、新しく読み始めたすべての処理が終わるまで、無期限に待ち続けるわけではないという点だ。既存の読み手が安全に処理を終えられるだけの猶予期間を確保する。
今回の方式で保証するのは、参照カウントが常に残っていることではなく、読み出し中のメモリが別の用途に再利用されないことだ。
以前にも、参照カウントを増やさずに読み出す方法は検討されていた。その案では、一時バッファにデータをコピーした後、取得した内容が正しいかどうかを検証しようとしていた。しかし、メモリがいったん解放され、別の用途に使われた後、再び同じ位置に割り当てられるような状況への対処が難しかったという。今回の設計は、メモリの再利用そのものを遅らせることで、この問題を回避しようとしている。
実際の読み出し処理を変更するパッチには、高速経路を利用するための条件も細かく設けられている。folioが分割されても誤ったページをコピーしないよう、データはページ単位で扱う。必要なデータがページキャッシュに存在しない場合や、先読みなどの追加処理が必要な場合には、従来の読み出し経路へ切り替える。
したがって、Linuxの参照カウント管理を全面的に廃止する変更ではない。
また、RCUの読み取り区間内でページフォールトによる待ちが発生しないよう、コピー中はページフォールトを無効にしている。コピー先のユーザーメモリが利用できない場合、高速経路でのコピーを中断し、従来の経路に切り替えてページフォールトを処理してから読み出しを続ける。
ただし、この切り替えには性能上の代償がある。
10月10日の測定では、コピー先のユーザーページが利用できず、ページフォールトの処理が必要になる16バイト読み出しのケースで、1スレッド当たりのサイクル数が4,117〜4,188から6,543〜6,593へと増加した。
つまり、ページキャッシュにデータが存在していても、コピー先のメモリの状態によっては従来より遅くなる。今回の改善効果を評価するには、高速経路を利用できる条件も考慮する必要がある。
メモリの解放待ちが新たな問題に、CMAにも性能回帰
初期の6本のパッチだけを適用した試験では、メモリ不足によってプロセスが強制終了される問題が発生した。
これは、スワップを無効にしたメモリ2GiBの仮想マシンで、ファイル領域の確保と解放を繰り返す処理と、大きな匿名メモリ領域にアクセスする処理を同時に実行した際に確認されたものだ。
再利用待ちのメモリが存在するにもかかわらず、カーネルが利用可能なメモリの状況を適切に把握できず、メモリ不足を検知したOOM killerがプロセスを終了させてしまった。
この問題に対処するため、後半のパッチでは、CPUごとに蓄積された解放待ちのメモリが増えた場合、RCUの猶予期間が早く終了するよう促すとともに、解放処理にCPU時間を割り当てる仕組みを追加した。
4つのプロセスがそれぞれ64MiBのファイル領域を繰り返し確保・解放するテストでは、解放待ちのメモリ量が平均565MiBから15MiBまで減少した。また、8本すべてのパッチを適用すると、先ほどの仮想マシンで問題を再現したテストも、途中で停止せずに完了するようになった。
しかし、メモリ管理に関する問題がすべて解消されたわけではない。
Torvalds氏が挙げた既知の問題には、解放待ちのページが空きメモリなどの管理カウンターに反映されない点が残っている。負荷を16プロセスまで増やしたテストでは、全パッチを適用しても平均177MiB、最大870MiBのメモリが再利用待ちの状態になった。
読み出し処理での競合を減らす一方、解放を待つメモリをどのように管理し、どこまで蓄積を許容するかが新たな課題となっている。
さらに、物理的に連続したメモリ領域を確保する仕組み「CMA(Contiguous Memory Allocator)」でも、従来は発生しなかった問題が確認されている。
今回の実装では、移動元のページが解放待ちの状態に残るため、alloc_contig_range() が、以前ページキャッシュに使われていた領域の確保に失敗することがある。メモリの解放完了を待ってから確保するなどの対策は、説明時点では実装されていない。
そのほかにも、splice() でページの所有権を移す処理をコピー方式に変更した影響や、s390アーキテクチャの保護されたメモリとの競合が検討課題に挙がっている。メモリ解放が頻繁に発生するワークロードでの総合的な処理コストも、まだ測定されていない。
読み出しが高速化された一方で、その負担が別の処理に移っただけではないか、慎重に調べる必要がある。
Claude Codeはどこまで関与したのか、Linux本流への採用は未定
Torvalds氏は今回の説明文で、コードやテストプログラムの作成だけでなく、既知の問題を整理するためのレビューや説明文の初稿にもClaude Codeを利用したと明かしている。
いずれも自身の指示に基づいて進め、性能測定は本人のマシンで実施したという。読み出し処理を変更するパッチには、AIの支援を受けたことを示す Assisted-by: LLM と、Torvalds氏自身による Signed-off-by が記載されている。
ただし、AIがコードやテストの作成を支援したという事実だけでは、検証の独立性が担保されているとはいえない。
今回の試験では、ページキャッシュからデータを追い出し、メモリを再利用する処理と並行して読み出しを行い、取得したすべてのバイトを検証している。30秒間で約1億回の読み出しを実行した結果、データの誤りは検出されなかった。
一方、遅延解放の仕組みを意図的に取り除いた初期版を使った対照試験では、誤ったデータの読み出しが検出された。この対照試験を現在の版では再実施していないことも、Torvalds氏は明記している。
未検証の項目も少なくない。ファイルシステムのテストスイート「xfstests」や、読み出し中にfolioが分割される状況は試していない。KASANによるメモリエラー検査、32ビット環境、別のマシンでの動作検証も実施されていない。
複数のCPUソケットを備えるサーバーでは、さらに大きな改善が期待できるという見通しも示されているが、現時点では実測結果がない。
Linux公式のツール生成コンテンツに関する指針では、コードの提出者が変更内容全体を理解し、レビュー時の質問に答えられることが求められている。利用したツールやその影響範囲、実施したテストを明らかにすることも重視されており、メンテナーは必要に応じて追加の検証を要求できる。
つまり、AIを利用したことを適切に開示していても、それだけでパッチの採用が認められるわけではない。
10月12日時点で確認したLinuxのメインライン(master)のログはLinux 7.3-rc7まで進んでいるが、今回の8本のパッチは専用の開発ブランチで公開された試作段階にあり、正式な採用時期は決まっていない。
今後は、CMAに生じた問題の解消に加え、メモリ解放処理の負荷測定や、異なるハードウェア構成での安全性検証が必要になる。これらの課題を解決できれば、同じファイルを多数のCPUが読み出す際に、参照カウントの更新による待ち時間を大幅に削減できる可能性がある。



