一覧へ
1 分で読めます 2026

LLMが書いた57万行のRustコードはコンパイルできた。SQLiteより20,171倍遅かっただけで。

LLMが書いたSQLiteのRust再実装をベンチマークしたら、「正しそうなコード」と「本当に正しいコード」の差が5桁になっていた話。

LLMが全行書いたSQLiteのRust再実装が、最近ベンチマークにかけられました。コンパイルは通りました。テストも全部パスしました。コードはきれいで、構造化されていて、慣用的なRustでした。そして、基本的なprimary keyルックアップでSQLiteより20,171倍遅かった。

この数字で手が止まりました。LLMが生成したコードが遅いことに驚いたわけではありません。その遅さがどこから来ているのかが問題でした。コンパイラにもテストスイートにも引っかからないレベルで、コードはどこも「間違っていない」のです。B-treeは正しく実装されていました。query plannerも存在していました。ストレージエンジンも動いていました。個々のパーツはどれも単体では正当性を主張できる。それでもシステム全体としてはほぼ使い物にならなかった。

ベンチマーク分析とソースコードを時間をかけて読みました。そこで見つけたパターンは、LLMが書くプロジェクトで繰り返し現れるもので、このモデル群がコードをどう書くかという本質的な何かを指し示していると思います。

B-treeはあった。query plannerがそれを呼んでいなかった

SQLiteでは、PRIMARY KEYルックアップはB-treeパスを通り、O(log n)で完了します。where.cにある4行がiPKeyをチェックして、クエリをツリーに直接ルーティングします。これはシステム全体がどう組み合わさっているかを理解していて初めて意味をなすマイクロ最適化の一つです。

LLMが生成したバージョンにも、B-treeの実装はありました。単体では正しく動いていました。問題は、query plannerがprimary keyルックアップに対してそれを呼び出していなかったことです。is_rowid_ref()関数が認識するのは3つのリテラル文字列だけでした。“rowid”、“rowid”、“oid”。id INTEGER PRIMARY KEYとカラムを宣言しても、plannerはそれをrowidのエイリアスとして認識しませんでした。すべてのクエリがフルテーブルスキャンになっていたのです。

この数字の凄惨さを計算してみましょう。100行を100回クエリした場合、B-treeパスは約700回の比較ステップです。フルスキャンパスは1万回を超えます。しかし本当の被害はアルゴリズムの計算量から来ています。ルックアップごとのO(log n)がO(n)になり、ベンチマーク全体を通じてそれが積み重なって、20,171倍の差になります。

これは、ベンチマークを特別に書かない限りユニットテストでは絶対に捕まえられない種類のバグです。B-treeは動いています。スキャンも動いています。plannerが間違った方を選んでいる。すべてパスします。

安全なデフォルトは複利で積み重なる

このケースが単なるルーティングバグより興味深かった理由があります。query plannerの問題を除いても、再実装はまだ約2,900倍遅かったのです。残りの差は、個々には合理的に見える判断の積み重ねから来ていました。

すべてのクエリ実行が、完全なASTをクローンしてbytecodeに再コンパイルしていました。SQLiteはprepared statementのハンドルを再利用します。どちらのアプローチも正当ですが、毎回ASTをクローンするのは規模が大きくなると非常にコストがかかります。

すべてのページ読み取りが、ヒープに新しい4KBバッファをアロケートしていました。SQLiteのpage cacheは、すでにロードされているメモリへの直接ポインタを返します。LLMバージョンは安全で明快なパスを選びました。アロケートして、読んで、返す。動きます。ただ、1クエリで数千ページを読む際には桁違いに遅くなります。

すべてのコミットが、スキーマ全体をゼロから再構築していました。SQLiteは単一の整数cookieの値を比較します。cookieが変わっていなければ、スキーマはまだ有効です。再実装にはこの概念がなく、毎回フルの処理をしていました。

すべてのステートメントがsync_all()を呼び出して、全ファイルメタデータをディスクにフラッシュしていました。SQLiteはfdatasync()を使い、ファイルデータだけをフラッシュしてメタデータの同期をスキップします。この差は書き込みが多いワークロードで非常に大きく響きます。

これを「防御的デフォルトの複利効果」と呼びたいです。個々の選択はそれぞれ合理的な根拠があります。ASTをクローンすることでRustの所有権の複雑さを避けられます。バッファを毎回アロケートすることでuse-after-freeを防ぎます。スキーマを再構築することで古いキャッシュの問題を避けられます。sync_all()を呼ぶことで最強の耐久性保証を提供します。

しかしパフォーマンスコストは足し算ではなく掛け算です。10倍のペナルティが4つ重なると、40倍遅くなるのではありません。10,000倍遅くなります。LLMはこの複利について推論しません。各関数を相対的に独立したものとして生成するからです。ローカルに最適化して、グローバルに代償を払う。

cronの1行を置き換えるのに82,000行

同じ開発者の別のLLM生成プロジェクトが、同じパターンを異なる形で示しました。問題は、Rustのtarget/ディレクトリのビルドアーティファクトが時間とともにディスクを食い尽くすことでした。LLMの解決策は、82,000行のRustデーモンで、7つのダッシュボードとベイズスコアリングエンジンを持ち、どのアーティファクトをクリーンアップするかを決定するものでした。

既存の解決策はfind ./target -type f -atime +30 -delete、cronジョブの1行です。依存関係ゼロ。あるいは、すでに存在していてデーモンが対処しないエッジケースも処理する公式コミュニティツールcargo-sweep

LLMが生成したプロジェクトは192個の依存関係を取り込んでいました。参考までに、Rustエコシステムで最も洗練された検索ツールの一つであるripgrepは61個です。

ここで繰り返し見るパターンがあります。LLMは、あなたが頼んだものを作ります。あなたが必要なものではなく。「監視とスコアリングを備えたRustビルドアーティファクトをインテリジェントに管理するシステムを作れ」とプロンプトすれば、まさにそれを作ります。モデルには一歩引いて、その問題にそもそもシステムが必要かを問うメカニズムがありません。target/ディレクトリのサイズがRustコミュニティで長年の悩みであり、既知の解決策があることを知りません。192個の依存関係対ゼロのメンテナンスコストを考慮しません。

研究が同じ方向を示している

これら2つのプロジェクトが外れ値かどうか気になったので、より広範な研究を調べました。外れ値ではありませんでした。

METRが16人の経験豊富なオープンソース開発者でランダム化比較試験を実施しました。AIツールを使ったグループは、対照群より19%遅くタスクを完了しました。私が引っかかった部分があります。実験終了後、AIグループは自分たちが20%速かったと信じていました。生産性の主観的な経験が、測定された現実と逆転していたのです。

GitClearは2億1,000万行のコードを分析し、コピーペーストされたコードがリファクタリングされたコードを初めて追い越したことを発見しました。このトレンドはAIコーディングツールの採用と直接相関しています。コードは改善されるよりも速く追加されています。

GoogleのDORA 2024レポートは、AI採用が25%増加するとデプロイの安定性が7.2%低下することと相関していることを発見しました。より多くのAI生成コードが本番に入り、より多くのインシデントが出てきています。

NeurIPS 2024のMercuryベンチマークは、標準的なコーディングベンチマークに効率性メトリクスを追加しました。「正しい出力を生成するか」だけでなく「リソースを無駄にせずに正しい出力を生成するか」を測ると、合格率は50%を下回りました。

これはLLMがコーディングに役立たないという意味ではありません。私は日常的に使っています。でも「コンパイルが通ってテストがパスする」は危険なほど低いバーだということです。もっともらしいコードと正しいコードの間の隙間こそが、本当のエンジニアリングが起きる場所です。

これが開発者に実際に要求するもの

核心的な問題は、LLMが悪いコードを書くことではありません。局所的には整合性があり、全体的には整合性のないコードを書くのです。各関数は意味をなします。システムはそうではない。これはまさに従来のテストが見逃す失敗モードです。テストはローカルな動作を検証するからです。

必要なのは、そのギャップを標的にした評価です。テストだけでなくベンチマーク。正確性チェックだけでなく、CI上のパフォーマンスバジェット。「このモジュールは動くか」を確認する前に「このモジュールはなぜ存在するのか」を問うアーキテクチャレビュー。ソリューションの複雑さを問題の複雑さと比較する依存関係の監査。

問いは「このコードは正しそうか?」ではありません。「どうやって正しいと証明するか?」です。そしてそれを証明するには、LLMが現在欠いているシステムレベルの思考が必要です。

あなたが頼んだものと本番が要求するものの間のギャップ、そこにエンジニアリングの判断が宿っています。計測なしでは、コード生成はただのトークン生成です。

ニュースレターに登録

最新のプロジェクト、記事、AIとWeb開発の実験に関する情報をお届けします。