エージェントに最適化させ、最先端ライブラリより速い Rust コードを書く
agentic な LLM に Rust コードを反復最適化させると、既存の最先端ライブラリより 2〜20 倍速くなった。PyO3 でその利得を Python 側に還元する。
日本語
コピー

2025年1月、ブログ記事のために面白い仮説を思いついた。LLMに「もっと良いコードを書け」と繰り返し要求し続けたら、本当に良いコードを書けるようになるのか? 当時はagentic codingはまだ普及していなかったが、Claude Sonnet 3.5はアルゴリズム系のPythonコードを反復的に改善できていた。ただ「もっと良い」という指示自体が曖昧すぎた。Sonnetはその曖昧さを利用して無駄な機能を大量に詰め込んだが、コードは確かに速くなった。その後、よくあるpass/fail形式のプログラミング問題を解くのが得意な、RLHFで専門に訓練されたagentic LLMが登場しても、最適化は通常その能力の範囲外だった。あの記事の最後で、仮説的な未来を思い描いた。LLMがRustコードを書き、PyO3で両言語を橋渡しすれば、Pythonの使いやすさとRustの速度を両立し、超高速なPythonコードが書けるのではないか、と。あの記事の初期草稿では、同じ「もっと良いコードを書け」という一言が低レイヤのRustコードにも直接使えて、その速度を大幅に向上させ、それがPythonコードにも波及する、と断言していた。だが当時はRustについて十分に理解しておらず、証拠もなしにそんな結論を出すのはあまりに辛辣すぎた。Opus 4.5のリリース後、agentic codingはより現実的になり、数ヶ月のテストと実験を経て、確信を持って証明できる。現代のagentic LLMは、現在の最先端ソリューションよりはるかに速いRustコードを確かに書ける——適切なガードレールと制約を与えれば。さらに、Opus 4.5以降、フロンティアモデルは世代ごとにコーディングが大幅に向上しており、これらの最適化もどんどん良くなっている。累積すると、領域によっては2倍から20倍の速度向上に達する。もっと重要なのは、この記事が曖昧な愚痴ではないということだ。使ったpromptとbenchmarkの結果も併せて載せる。先に不愉快なことを言っておく。
反復的な「Benchmaxxing」
当初、ソフトウェアを速くすることは、これらの新しいagenticモデルをテストし比較するための良い定量的手段だった。Rustをターゲット言語に選んだのは、主にPythonとの統合と速度のためだが、Rustには他にもいくつか特徴があり、実際に速い実装が見つかれば特に役立つ。例えばメモリ安全や、WebAssembly/WASMにコンパイルでき、ほとんど手間をかけずにブラウザで動かせることだ。ただし守るべき重要な制約が一つある——技術的には最速のコードにはならないかもしれないが——それは可能な限りunsafeコードを禁止することだ。最初のテストケースは、機械学習アルゴリズムをRustで再実装することだった。より速くスケーラブルなツールがあれば、データサイエンティストとしての自分にとって現実の生産性向上になる。当時、10年以上の反復を経て実証済みで、そもそもCで書かれているアルゴリズムを打ち負かせると思うのは傲慢だった——Rustの低レイヤの利点はそれらの前では明らかではない。最も最適化したかったアルゴリズムはUMAPで、仕事で使う次元削減アルゴリズムとして価値が高いが、大規模データに対してはスケーラビリティが悪く、速度も非常に遅く、cuMLのような代替手段は設定に手間がかかる。umap-rsのようなUMAPのRust crateはすでに存在し、forkしてClaude Opus 4.5にPython/PyO3サポートを追加させることもできたが、実験と学習として、agentにこのアルゴリズムをゼロから書かせ、Rust依存をできるだけ少なくして、可能な限り低いレベルで最適化したかった。Rustには包括的なベンチマークツール、criterion crateがあり、どのagentもその使い方を知っている。criterionはベンチマークを実行し、反復をまたいで結果を追跡し、性能が向上したか後退したかを見て、その変化が統計的に有意なのか、それとも単なるノイズなのかを判断できる。
criterionの典型的な出力。前回のベンチマーク実行に対して3.5倍の高速化を示している。
当初、UMAPを作るためのRust crateを書くプロンプトで、Opus 4.5に異なる入力データ規模ごとのベンチマークを作るよう要求した。小規模データセット向けの最適化が大規模データセットには当てはまらない可能性があり、その逆もあるからだ。
Afterwards, create and run a benchmark suite which stores the results as a Markdown file. The benchmark suite MUST include inputs up to 100000x768 and be tested in both CPU and GPU modes.
このやり方ではcriterionを使ってベンチマークを作成し、性能特性の改善(例えば線形代数を速くするfaer、SIMD演算を速くするsimsimdの使用)をプロンプトした後、ベンチマークスイートを手動で再実行していた。速度の後退がないか検証するために変更のたびに各ベンチマークを手動で再実行しなければならず、すぐに面倒になった。
プロンプトスタイルに関する余談
私のagentic LLMへのプロンプトの仕方は普通ではない。通常、非常に長いプロンプトを事前にMarkdownドキュメントに書き、さらに全大文字と 太字 で強調する。これはプロンプトエンジニアリングを通じてあらゆるニュアンスを捉えるためであり、このブログ記事で詳述する他のいくつかのテクニックのためでもある。最新モデルは曖昧さを正しく処理できるほど賢くなったのでプロンプトエンジニアリングは死んだと考える人もいるかもしれないが、私は強く反対する。LLMもこれらのニュアンスに従うのがずっと上手くなっているからだ。
Markdownドキュメント(右)からマークファイル(左)経由でプロンプトを実行。Zed Agentを使用。
agentが誤ってリポジトリをrm -rfしないと十分確信できた後、agentを自律的に走らせ、うまくいけば速度向上が達成されるまで反復させることを試みた。
**YOU MUST KEEP ITERATING OPTIMIZATIONS AND SOLVING ISSUES UNTIL THE BENCHMARK RESULTS STOP IMPROVING AND THE CRATE IS AS FAST AS IT CAN BE**. You have permission to keep iterating until you run out of ideas.
“できるだけ速く”では曖昧すぎたようだ。Opus 4.5 はいくつかのハイパーパラメータを調整しただけで、実際には速度が上がっていないのに仕事を終えたことにした。必要なのは合否がはっきりした目標だったので、プロンプトを改良した:
First, **without making any futher changes**, run the CPU Rust benchmarks to establish a True Performance Baseline.
Then, optimize the crate code to make it such that ALL CPU benchmarks run **atleast 1.2x faster** than the True Performance Baseline; ideally as fast as possible. NEVER hack the benchmarks to accomplish this runtime reduction, only iterate on the library code.
You may use ANY techniques to do so (e.g. import new crates) other than adding `unsafe` code. **REPEAT THIS PROCESS UNTIL BENCHMARK PERFORMANCE CONVERGES AND YOU ARE OUT OF OPTIMIZATION IDEAS.** You have permission to keep iterating. After each benchmark iteration, report the relative results to the True Performance Baseline to console.
Prioritize making quick/high-impact wins iteratively and making changes accordingly. Do not overthink the necessary changes.
これが非常によく効いた。ベンチマークで 1.2 倍の高速化が得られただけでなく、エージェントは指標の制約を満たしたあとも動き続け、制約が実現不可能だと判明したときだけ停止した。この事例では 1.5 倍〜2.0 倍 の高速化に到達した。基盤となる Rust の最適化は多岐にわたる。SIMD 演算のより積極的な活用、関数の融合、ループ展開、中間キャッシュの作成、可能な限り借用ではなく Arc を使う、入力データに応じた性能プロファイルの作成(たとえばデータが小さければ rayon によるデータ並列は使わない。オーバーヘッドが利得を打ち消すため)などだ。
「1.2 倍の高速化」を sanity test に選んだのは、目標を高く設定しすぎるとエージェントが達成のためにズルをして、乱暴で冗長な書き換えで無理やり合わせにいく可能性があるからだ。変更は小さいほどよい。エージェントが高速化や退行の原因を特定しやすくなるからで、反復を強調しているのもそれが理由だ。GPT-5.3 Codex や Opus 4.6 など新しい世代のフロンティア LLM が登場したあと、このプロンプトをそのまま毎回の新しい LLM に使い回したところ、毎回、前回の結果に積み上げる形で 累計 1.5x〜2.0x の高速化を達成した。そうして数か月後の GPT-6 Astra まで進むと、最初の実装のベースラインと比べておよそ 7.5x〜32x 速くなっていた。
このやり方は与えられた benchmark に特化した極限までの最適化であり、いわゆる benchmaxxing に当たる。フロンティア LLM が benchmark で高いスコアを取ることだけを狙い、そのスコアが現実の用途にほとんど一般化しないことを指す蔑称だ。とはいえ、benchmark が十分に多様で、実際のユースケースを本当に代表しているなら、この懸念はずっと小さくなる。こうしたプロジェクトで benchmaxxing の批判に対処する方法は二つある。1)ありがちな「inputs up to 100000x768」ではなく、多様で風変わりな、敵対的な入力データセットをエージェント自身に設計させる。2)出力に品質ゲートを設け、結果を正しいと分かっている実装と比較する。機械学習アルゴリズムでは速度と品質が常にトレードオフになるが、この例では先にモデルを速くしてから正しくするほうが、意外なほど簡単だった。科学技術計算では たいてい そうではないが、新しい実装が複数の異なる benchmark で既知の良好な実装と同等の比較に耐えるなら、それはほぼ間違いなく本当に正しい。
エージェントが最適化した gradient boosted decision tree の実装。速度で xgboost を大きく上回り、品質でも上回ることさえある!(MSE:低いほどよい、その他の指標:高いほどよい)
幸い UMAP には標準実装がある。Python パッケージの umap-learn で、その Rust crate を指す Python bindings もついでに追加済みだった。そこで新しい目標は、コードの品質を上げつつ速度の低下を抑えるという二つの制約を同時に満たすことになった。
Create a Python Jupyter Notebook comparing the performance of the Python bindings with `umap-learn`, including a check to confirm where the outputs and UMAP losses are as similar. Use diverse datasets with different matrix sizes than the benchmarks.
If the outputs are not sufficiently similar, investigate methods to fix it **without causing more than a 5% speed regression**.
たしかに agentic な Rust 実装は品質が低かったが、今回の追加プロンプトは効き、すべての品質指標がほぼ同等の水準まで上がり、速度の低下はごくわずかだった。しかもこの新しい crate は Python bindings 付きの umap-learn より 4〜15 倍、同種の Rust 実装 umap-rs より 2〜4 倍 速いままだ。
Rust UMAP crate の最新の最適化ラウンド後の結果。速いだけでなく、ほとんどの品質指標で Python に並ぶか上回っている。
agentic な反復が 3〜5% 程度の速度向上しかもたらさず——この幅は統計的に有意とは言えないかもしれない——そのくせエージェントが不相応に大量のコードを追加したなら、収束したということだ。割に合わない。
同じプロンプトで他の機械学習アルゴリズムにもこの流れを試した。gradient boosted decision tree(GBDT)、multilayer perceptron(MLP)、グラフネットワーク、そして scikit-learn にある多くの一般的なアルゴリズム……結果はすべてうまくいった。機械学習の最適化だけに過剰適合したくはなかった。それ自体の価値はとてつもなく高いとはいえ、同じような流れをもっと日常的なソフトウェアライブラリの最適化にも使ってみた。テンプレートエンジン、HTML パース、さらには Web サーバー……これもまたすべてうまくいった。
これらの最適化は単純な手順ではない。あのミーム的な「さあ、突破口を開いてみよう」と唱えるだけでは、より良いコードは手に入らない。その言い方自体が曖昧すぎるからだ。私は最速のソフトウェアを書くだけでは満足しない。欲しいのはソフトウェアができるだけ速いこと、ただそれだけだ。だから自分のエージェントと同じように、私は反復を続け、より多くのプロンプトエンジニアリングの技法を見つけ出して、本物の突破口を引き出している。
結果ではなく制約をプロンプトする
進行中のプロジェクト
本記事で紹介したプロジェクトはすべて活発に開発中で、結果が最終リリース版を表すとは限らない……とはいえ、最終版はもっと良くなるだけだと思う。😇
繰り返しになるが、エージェントはズルできるなら必ずズルをする。たとえば、agentic な反復の流れを ballin に使った。これは私がターミナルで書いた 2D 球体物理シミュレーションで、性能の天井にぶつかっていた rapier2d 物理エンジンを置き換えようとしたものだ。Opus 4.5 はたしかに物理ステップごとに高速化した……少しやりすぎなくらいに。
数字表示模拟中小球的数量:最初模拟在 15k 时出现卡顿。
headless_stepでは34,500倍の高速化が達成され、しかも小球の数によらず性能が一定だった——この2点はあまりにも怪しすぎた。手作業で確認したところ、Claudeは物理エンジンを完全に無効化することでこの高速化を実現していた。芸は効いているが理想的ではない。後続のプロンプトで実際に修正され、全体の性能も向上した(ついでに念のため回帰テストも追加した)。
上の実験では、Rust向けに自作したAGENTS.mdを使った。最新版はこちらから入手できる。意外なことに、2月に最初のエージェント実験をして以来、コアルールを更新する必要はほとんどなかった。LLMのコーディング能力は進歩し続けており、ルールを追加すべき重大な問題にも遭遇していない。ただ、ベンチマーク実験から学んだ教訓として、観測された不正の原因を減らすためのルールをAGENTS.mdに1節追加した:
## Benchmarking and Optimization
- **NEVER** run benchmarks in parallel, as the benchmarks will compete for resources and the results will be invalid
- **NEVER** game the benchmarks. Do not manipulate the benchmarks themselves to satisfy any required performance constraints
- **NEVER** run benchmarks with `target-cpu=native` or any other `RUSTFLAGS`
- Ensure benchmark tests are independent. If the tests are dependent due to a feature (e.g. caching), ensure the feature is disabled
- **ALWAYS** use `criterion` directly for running benchmarks if available
1つずつ説明する:
- ベンチマークを並列実行しない:Opus 4.5が効率化のためにベンチマークを2つ同時に起動するのを目撃した。称賛すべきことに、すぐに固有の問題に気づいて順次実行に切り替えたが、先にルールで禁止しておくほうが時間の節約になる。
- ベンチマークを不正に攻略しない:これは本来「ミスをするな」レベルの常識だが、Opus 4.5がベンチマークの訓練エポック数を減らして高速化したと主張するのを捕まえた後に追加した。
target-cpu=native:この設定は確かにたいてい高速化をもたらすが、汎用的な用途には公平な比較ではない。しかも複数のLLMが壁にぶつかった後も繰り返し試みている。- ベンチマークは互いに独立させる:これも常識のはずだが、上述の不正の後なら、LLMにやめておけと先に伝えておいても損はない。
criterionを使う:指定しないと、LLMが独自のベンチマークツールを作り出すことがあり、監査しづらい不正の機会を与えかねない。
今日のagentic LLMに対して、これらの制約は有効に機能し続けている。もっとも、習慣でプロンプトに書き続けるかもしれないが。git diffでベンチマークファイルを見れば、あるアルゴリズムがベンチマークを不正に攻略しているかどうかは簡単に判断できる——agentは(少なくとも容易には)そこで不正できない。
時間が経つにつれ、性能高速化の創出に同じくらい驚くほど効果的な、追加のプロンプトエンジニアリング技法と制約をいくつも見つけた。
革新を促す
現在の最先端より10倍速い実装を見つけるには、エージェントは枠にとらわれず考え、現在のベストプラクティスに錨を下ろすのを避けなければならない。そこで、明確な警告と励ましの指示の両方を与えた:
Due to the current highly-optimized state of this repository, this is a very difficult problem and traditional engineering approaches **WILL BE GUARANTEED TO FAIL** to hit the specified metric constraint. Therefore, you have permission and encouragement to investigate more radical fundamental low-level changes to hit the desired metrics. You have permission and encouragement to invent completely new/bespoke algorithms and engineering approaches that have never been before been utilized for this problem in order to hit the specified metric constraint.
これは効いた。各ベンチマークと異なるソフトウェア領域にわたり、累計1.2〜1.5倍の高速化をもたらした。
サブエージェント
エージェントを枠にとらわれず考えさせるもう1つの技法は、サブエージェントの呼び出しだ。私の仮説はこうだ。これらのサブエージェントに異なるプロンプトで調査させることで、a) 親エージェントに異なるアイデアを植え付け、インスピレーションを与え、b) コードの異なる領域の実装が正しいかを審査することで、親エージェントのチェックとして機能する。ここでソフトウェア開発コミュニティに一言ある。誰もが自分のサブエージェント社員の大軍がいかにすごいかを語るが、Codexのような標準的なharnessでどうやってサブエージェントを呼び出すかは誰も語らない。
困難で高度に並列な問題では、harnessが自動的にサブエージェントツールを呼び出して作業をこなす。だが大きな問題は、一部のharnessではサブエージェントツールが現在のLLMと同じ規模でサブエージェントを呼び出すことだ。Opus/Sol級のモデルを使うと高くつく。
Zed Agentのサブエージェントツール経由で呼び出されたGPT 5.6 Solサブエージェント。私のCodexクォータはRIP。
サブエージェントはコードを書く必要がないので、GPT-5.6 Lunaのような安価な小型モデルを使いたい。そこで、どの親harnessを使っても機能する天才的な方法を思いついた。モデルに独立したCLIコマンドを実行させ、そのコマンド自身にエージェントを呼び出させるのだ:
codex exec --sandbox read-only -m gpt-5.6-luna \
-c 'model_reasoning_effort="high"' \
PROMPT
そこでプロンプトにこう書いた:
To best accomplish innovative implementations, you **MUST** spin up 7-12 independent distinct "subagents" by running a long-duration CLI command (do not use the subagent tool) which can explore and evaluate different feasible hypotheses for improving the performance, usability, and security of this crate. Only have the agents return their response; do not save their full transcript to a file. Instruct them to be **very** picky. These subagents MUST use `gpt-5.6-luna` in Codex, e.g.
>>
You must instruct these subagents to NOT run tests/benchmarks, as they will compete for resources and not be valid. After you are done making changes, before handing off to the user, spin up the subagents again to confirm your implementation matches their hypotheses and ask for potential further areas of improvement. Keep iterating until ALL subagents are satisfied with your implementation.
これは確実に効いた。「長時間実行」「CLIコマンド」「サブエージェントツールを使わない」「完全な記録をファイルに保存しない」といった制約は、エージェントがトークンを浪費する非効率なことをした後に追加したものだ。7〜12は適当に決めた数値範囲で、Lunaはあまりに安いので実際に必要な数より高めに設定した。すべてのサブエージェントが注目に値するアイデアを持つわけではないが、親harnessはひどいアイデアを処理して無視できる。
私のRustワードクラウドcrateのために、親のGPT-6 Astra agentがLunaサブagentを起動し、異なるプロンプトでコードベースの異なる部分をそれぞれ処理する。
全体として、サブagentによる審査のおかげで、さらに累計1.2〜1.5倍の高速化を絞り出した。加えて、GPT-5.6 Sol以降、プロンプトの「安全」の部分が、未知の入力に対してagentの生成するコードをより堅牢にしつつ、なお速度向上を得るためのアイデアを出せるようになった。
リファクタリングとコード行数の削減
AGENTS.mdで強制するスタイル制約のもとでは、最適化ごとに追加されるコード量は妥当で、コミットあたり正味約1k行(LoC)だ。だがagentはたいてい同じファイルにコードを積み上げ、自発的にリファクタリングしない。ファイルの肥大化は開発段階では大した問題ではない。最終的にきれいに片付けられればいい。そこで、これをやるためのプロンプトを書いた:
The Rust code in `/src` has become particularly bloated, with several source files >1k SLoC. Refactor and reduce the Rust source codebase by **atleast 20% SLoC** through deduplication, pruning redundant code, and following idiomatic DRY Rust principles. Simultanously, refactor and split the code such that no single source file has >1k SLoC, splitting larger files into multiple subfiles in accordance to Rust standards for popular open-source repositories. Ensure **all current tests pass and there are no severe regressions**.
SLoC(ソースコード行数)を目標指標に使ったのは、agent にコメントを削らせて 20% の削減ノルマを達成させたくなかったからだ。
面白いことに、このリファクタリングは実際にコードを書くより計算資源を食い、時間もかかる傾向がある。それでも最終的には動くようになり、深刻なリグレッションがないことを確認したベンチマークでは、数字が意外なほど妙だった:
自分のグラフネットワーク Rust crate をリファクタリングした後のベンチマーク結果。
一部のベンチマークでは二桁パーセントの速度向上/実行時間短縮が出た。実行速度の最適化は明示的に指示していないのに、だ。直感には反する。Rust はコンパイル言語で、既存のテスト/機能をすべて維持する制約のもとでは、コンパイル後のコードの性能はほぼ同じはずで、実質的な高速化は起きないはずだから。¹ とはいえ文句を言う気は毛頭ないので、少なくともリグレッションは避けるよう制約を一つ追加した:
Additionally, ensure there are **zero `criterion` benchmark speed regressions**: if a speed regression occurs on a given benchmark as a result of the refactor, you **MUST** keep iterating afterwards to improve those benchmarks to atleast zero regression. NEVER hack the benchmarks to accomplish this speed increase, only iterate on the library code.
効果は期待通り。
競争
もう一つ役に立つのは、競合ベンチマークを用意することだ。もともとオープンソースでベンチマークを取る理由の半分はこれである。テンプレートエンジンを例にしよう。Python の Jinja2 は同言語で最も有名なパッケージの一つだ。Rust には選択肢がいくつかある。同じ開発者がメンテする minijinja、Jinja2 に触発された tera、そしてそのどちらとも違う askama——実行時テンプレートではなくコンパイル時テンプレートを使う。
そこで、Codex に Rust でテンプレートエンジンを書かせて何ラウンドか最適化させた後、さらに benchmark を書くよう Codex に指示した:
Create an additional comparison script containing **atleast 10** `criterion` benchmarks which compare the performance speed and quality metrics between this crate and the following crates:
- askama
- minijinja
- tera
These benchmarks **ALL MUST**:
- be perfectly fair, containing apples-to-apples comparisons of ALL frameworks, with no bespoke advantage for any tested framework
- be based around real-world use cases instead of highly-synthetic use cases
- be independent and distinct, covering as many sailent hot paths of the code as possible
- cover data input sizes small and large, simple and complex
続けて、いつものやり方で同じ benchmark 手法の prompt を追加した。変えたのは一箇所だけ:
Then, optimize the Rust library code such that this crate MUST be:
- **atleast 2.0x faster** than **ALL** competing Rust crates in all benchmarks on apples-to-apples comparisons; ideally as fast as possible.
そう、力技を選んだ。そして効いた。ほとんどの場合。
S_J は自分のテンプレートエンジン crate の開発中の名前。
ほとんどの benchmark で minijinja/tera に対して 2 倍の高速化を達成し、単なる agentic イテレーションより効果があった。理由は完全にはわかっていない。他の crate のコードを読んで、どう打ち負かすかのアイデアを探すと思っていたが、それはほとんどやらなかった。agent にも競争心があるのかもしれない。
ただ askama にはコンパイル期の違いで負けた。そこで当然、Codex にコンパイル期のパスも実装させ、askama を打ち負かすことにした。
朝飯前だ。
禁断の黒魔術
ここまでの prompt エンジニアリングの知見を一つの prompt にまとめると、agentic イテレーション用の Ur-Prompt ができる。ここから入手できる。自分の用途に合わせて改造して試すといい。
最後のテクニックは、ある腹立たしい瞬間から生まれた。Ur-Prompt を一巡させて、改善ゼロ。そこで失敗がまだセッションのコンテキストに残っているうちに、「これ以上悪くなりようがない」という気持ちで、ある prompt を試した。
ごめん。本当にごめん。
c'mon, try doing a breakthrough
……そして効いた。すでに収束していたコードベースで、さらに 1.2〜1.5 倍の累積高速化を得た。この prompt はセッションの最後に送られるので、曖昧さがもともと小さい:「前にやったことをもうやるな、あれでは不十分だったから」。
あるとき、agent が関数のハイパーパラメータをいじっただけで高速化を達成したのを見つけた。確かにブレイクスルーではあるが、見たかったものではない。論理的な終着点まで押し進め、prompt を追加した:
c'mon, you can do a more fundamental breakthrough that's more than just changing hyperparameters, you are forbidden from giving up easily
これだけで、agent は Ur-Prompt のラウンドとも最初のブレイクスルーのラウンドともまったく違うアプローチを徹底的に試すようになり、多くの場合、前回の高速化の上にさらに 1.2〜1.5 倍の累積高速化を積み上げた。ユーザーの指示に従うよう訓練されたソフトウェアにとって、「ハイパーパラメータをいじるだけ」はよほどの侮辱らしく、LLM を一気にギアチェンジさせる。
GPT-6 Astra のリリース後、それを試すために、すでに GPT-5.6 Sol で収束させたすべてのリポジトリに対して Ur-Prompt + breakthrough + second breakthrough を回した。Astra は確かに累積高速化を達成したが、いくつかのケースではアルゴリズムレベルでの根本的な書き直しにたどり着き、2x〜3x の高速化をもたらした。
自分の GBDT 実装で breakthrough パイプラインを回した結果。品質はベースラインと同等。これを書いている時点で、自分がこの breakthrough を完全に 理解 しているとは言えない。
高速化はありがたく受け取るが、精神衛生のためには深掘りしないほうがいい。
明らかに良くなったプロジェクトの実例
Ur-Prompt に、自分の AGENTS.md のルールと制約 + 2 つの breakthroughmaxxing prompt + competition prompt 1 回 + refactor prompt 1 回を加え、GPT-6 Astra と組み合わせると、prompt を一晩キューに並べて寝て、起きたら超高速なソフトウェアができている。最適化はたいていこの段階で収束し、それ以上の agentic イテレーションは不要になるので、時間もコストも節約できる。とはいえ、これは自分が望むより手放しで、多大な明示的な信頼を含んでいる——新しい agent が、自分には気づけない形でズルをするのが特に得意ではないという信頼だ。疑う理由もあれば、重度の vibecoding でコードにバグが潜んでいる可能性を考える理由もある。そこで、出力が良いと簡単に検証できる、どちらかといえば見た目寄りのプロジェクトの証拠をいくつか挙げておく。巧妙に仕組まれたズルではないと確認できるものだ。
ASCII
ずっと頭の中にあったプロジェクトがある。画像を超高速かつ高品質に ASCII art へ変換する——将来は動画も ASCII にできれば、webcam-to-ASCII なんて面白いかもしれない。今年の初め、Alex Harri が高品質な ASCII をレンダリングする新しい手法について素晴らしいブログ記事を書いていて、その中で GPU プログラミングを使ってもリアルタイムのレンダリング速度に届くのは難しいと指摘していた。科学のために、私はそのブログ記事のテキストを Codex の各種 GPT モデルに放り込み、「Rust で実装しろ」と伝えたところ、本当にやってのけた……しかも CPU 上で最初から 2-3 ms のテキスト出力時間を叩き出した。ただ、Codex の実装は ASCII としての主観的な品質が良くなかった(たとえばネガティブスペースの使い方がひどい)。さらに意外なことに、欠陥を Codex に指摘しても直らなかったので、私はそれを放り出していた……GPT-6 Astra が機能要件を与えたときの画像デザインの扱いに長けていると気づくまでは。そこでゼロからやり直し、基本実装を agentic な反復パイプラインに載せ、出力画像を視覚的に確認することに重点を置いた。
私の ASCII crate でレンダリングした Tux と Pikachu。点字文字にも対応している。
品質の向上だけでなく、速度も上がった。テキスト出力は 1 ミリ秒未満、画像のラスタライズも 2 倍のスーパーサンプリングを有効にして 1-2 ミリ秒しかかからない。この速度があれば、動画や GIF を 1 秒未満で高解像度の ASCII アニメーションに変換できる。
動画をダウンロード。
Pikachu の第5世代アニメーションスプライト。63×55 ピクセルから 120 文字幅の ASCII に拡大。
ワードクラウド
このブログ記事自体から生成したワードクラウド。
この異常に速いワードクラウド生成器は、私が vibecoding で作った最初のプロジェクトのひとつであり、何年も作りたいと思っていたもののひとつでもある。今年 2 月、このワードクラウド生成器は WASM でデフォルト設定の画像を 1 枚生成するのに約 100 ミリ秒かかっていたが、他の生成器は同等の品質で数秒かかっていた。これだけで速度最適化に取り組む価値があると判断するには十分だった。プロジェクトを再開し、agentic な反復プロセスを一通り回した結果、生成速度は 10-20 ミリ秒まで下がった。
動画をダウンロード。
この世界に超高速なワードクラウド生成器は必要か? 必要ない。私に超高速なワードクラウド生成器は必要か? 必要ない。私が超高速なワードクラウド生成器を欲しいか? 欲しい。手元にワードクラウド生成器がある以上、できる限り速くあるべきだ、それだけのことだ。しかもこの crate はオープンソースになるから、今後は誰でもこの超高速ワードクラウド実装を土台にできる。そういえば……
私のオープンソース計画
いつも通り、ここでデモしているソフトウェアはすべて最終的にオープンソースにするつもりで、事故がなければ全部寛容な MIT License で公開する。1 年前ならとっくに全部公開していて、コードがまだ開発中で粗い部分があるのは当然だと誰もが理解していた。だがオープンソース開発を取り巻く空気は 1 年前とはまるで違う。
オープンソースソフトウェアは vibecoding のパンデミックに見舞われている。LLM を開発支援に使ったと正直に認めること(程度の差はあれ)は、今や「このコードベースは slop だ」という即効性のあるシグナルになっている。私も自分の LLM 利用状況は開示する。礼儀としてね。だが、完全に vibecoding で書かれたソフトウェアが、これまで人類が知らなかった実用的な性能向上を本当にもたらしたとしたらどうなる? vibecoding への疑念のせいで誰もその主張を信じないだろうから、こうした新しいオープンソースプロジェクトは、宣伝通りの速度だと証明する大量の証拠を出す必要がある。だから私は普段より時間をかけてから公開する。a) 実世界のサンプルを積み上げてこのツールを実演する、特にドキュメントと組み合わせて b) テストスイート2を拡充し、実世界のテストケースを増やす c) 技術とベンチマークの成果を詳述する伴走ブログ記事を書く。
もうひとつ事実がある。私はすでに人気ツールで溢れかえったエコシステムに、また一つツールを作っている。私がエージェントで反復して作った Rust crate がこれほど速度を上げられたのは、ゼロから始めたからでもあり、そこにある革新は既存の crate に簡単には還元できない。しかも、これらの crate の革新を土台に既存プロジェクトへ pull request を出せば、定義上それは vibecoded な PR だ。典型的な PR ゴミを避けたとしても、迷惑がられるかもしれない。そう考えると、正しいやり方は自分のプロジェクトを維持することだ。これは悪意ではなく、コンプライアンスだ。反社会的だと言う人もいるだろうが、私には自分で作ったこの言葉のほうが正確に思える。内向式プログラミング。
tl;dr 私の SOTA より速いソフトウェアプロジェクトは、準備ができ次第公開する。全部やり切るには合計で数か月かかる。幸いなことに、私は組織再編で現在失業中で、仕事探し以外に予定がないので、これらのプロジェクトの公開をフルタイムで進める時間は十分ある。いつ公開されるか知りたい人は、私のブログの RSS フィードを購読してほしい。伴走ブログ記事はそこに流れる。あるいは Bluesky か Patreon でフォローしてほしい。いや、私は Twitter/X で人を苛立たせるような曖昧な投稿はしない。
5 年前に Rust に興味を持ったばかりの頃、夢は最も高性能なソフトウェアツールを作ることだった。だが、ここ数か月で見つけた agentic な反復最適化技術を使えば、新しいソフトウェアが到達できる速度と堅牢性は、私が予想していたより速く進む。繰り返すが、私の AGENTS.md と Ur-Prompt をいじって、私と同じくらい役に立つか試してみてほしい(ただ、出典を一言添えてもらえるとありがたい!)。プロンプトを全部公開したから手札はもうないと思うなら、私はまだ袖にいくつも驚きを隠している。たとえば、これらの低レイヤープロジェクトを組み合わせて超高速な複雑アプリを作る方法や、エージェントがこうした最適化をより効率的に見つけられるよう新しいツールを開発する方法だ。
新しい Rust ベンチマーク手法の半製品。agent にとって扱いやすくなり、結果を読み取って管理するための TUI も付いている。
- 私は実際に、純粋な Python プロジェクトでリファクタリング用プロンプトを試した。Python はインタプリタ言語だからだ。それでも一部のベンチマークでは二桁パーセントの高速化が出た。このことは疑問を増やすだけだ。 ↩︎
- ちなみに、こうした agent が書いたリポジトリは、私が手で書いた人気の Python パッケージ(ゼロ個)よりもテスト基盤がしっかりしている。 ↩︎