There's no reason for software to be slow anymore

Dan Luu

ソフトウェアが遅い理由はもうない

原文は Dan Luu により に公開されました。 このブログを購読する

先日、LLMが遅く肥大化したコードを生み出しているなどと語っている人たちはいずれ、すべてを超最適化されたアセンブリで書き直されたときに恥をかくことになるだろう、という趣旨のバズったツイートを見かけた。まだすべてをアセンブリで書きたいという段階にはないが、Nolan Lawsonがテストについて語った「今やバグを何個抱えるかは自分で選べる」という言葉の変奏が、私がこちらであまり気の利かない言い方で指摘した通り、パフォーマンスの領域でもますます現実になりつつある。

前回の投稿で、かつては専門的だったパフォーマンス改善のコストが何桁も下がったこと、そしてかつては希少なスキルを持つ個人やチームでなければできなかったパフォーマンス改善が、数文を入力できる人なら誰でもできるようになった1ため、かつては最大規模か最も収益性の高いプロジェクトでなければ割に合わなかったあらゆる最適化が可能になった、というコメントに対して、Marc Brookerはこう返した

最後の指摘には完全に同意する。あるクラスのワークロードではなく、特定のワークロードにフィットさせた動的なカスタムソフトウェアというのは、非常に起こりそうな帰結だと思う。(それ自体、いろいろと面白いリスクや機会を伴うわけだが)。ちょっとFFTWを思い出させる。それと、特定の問題(そしてしばしば特定のハードウェア)に特化して超高速かつ極小であることに全振りした、昔のデモシーンの奇妙なテクニックの数々も。例えば、コード自体をテクスチャとして再利用して優れたキャッシュ局所性を得ていたデモを覚えている。

また、Michael Malisはこう指摘している

「AIは役に立たない、なぜなら“コードはそもそも難しい部分ではなかった”からだ、というミームが広まっている。ある領域ではそれは正しいと思うが、別の領域ではコードを書くこと自体がまさに難しい部分だった。JITコンパイラはその良い例だ。多くのソフトウェアにとって、JITコンパイラはコードを高速化するのに大いに役立つ。JITコンパイラが稀であることは、歴史的に見てJITコンパイラの実装が難しすぎて割に合わなかったことを示していると思う。LLMは参入障壁を下げ、JITコンパイラを書くことをずっと容易にした。これがpgrustの背後にあるテーゼだ。データベースは歴史的に構築が最も難しいソフトウェアであり、そのために制約されてきた。今、AIによって、我々は構築するソフトウェアの種類についてより野心的になれる」

ワークロードのクラスへの最適化

これを、前回の投稿で作った正規表現エンジンであるFREで試してみよう。思い出してほしいが、FREはエージェントにrebar正規表現ベンチマークスイートへのアクセスを与え、1か月間ループさせて正規表現エンジンのパフォーマンスを改善させることで作られた。その結果、ホールドアウトのベンチマークがあることをエージェントに警告するまで、FREはrebarにひどく過学習していた。警告後はエージェントが最適化を十分に汎化させたため、ホールドアウトでもまずまずのパフォーマンスが出るようになった。ホールドアウトのベンチマークで十分にテストされた正規表現エンジンを上回れない「ソフトウェアファクトリー」製の正規表現エンジンを使う特段の理由はないが、FREで注目すべき点の一つは、ネイティブAOTコンパイル版が長い検索ではかなり良い成績を出したことだ。そこで、ripgrepが通常のマッチャーを動かしている間に別スレッドでネイティブコードコンパイラを走らせ、コンパイルが終わったらネイティブコードに切り替えれば、概ねパフォーマンスが向上するはずだ、と考えた。もちろん、コンパイルに1スレッドを取られるため、短いクエリではむしろパフォーマンスが悪化するが、数秒で終わる場合よりも何十秒、何分もかかる場合のripgrepの実行時間の方がはるかに気になるので、このトレードオフは許容できる。

正規表現エンジンを人間の作業時間で数分で作れたのと同じように、この実験も人間の作業時間で数分で試すことができる。数文を入力しただけで、エージェントが(人間ならそれなりの規模のコードの外科手術が必要になる)作業をこなしてこれを実現し、私のcodex履歴にある実際のripgrepクエリでベンチマークを実行した。より長いクエリでは、いくつかの非常に単純なクエリで2倍から4倍のパフォーマンス向上が見られた。ただ、ほとんどのクエリはより複雑で、代表的なホールドアウトクエリで実行すると、AOTを有効にすべきクエリでは2約7%の高速化が得られた。驚天動地の結果ではないが、codexに数分入力しただけで得られた成果としては悪くない(しかもまだ追加の最適化を続けており、おそらくさらに高速化するだろう)。

インデックスを作る?

これは議論の余地はあるが馬鹿げたことだとも言える。コンピュータ上で繰り返しテキストを検索するなら、高速化のための明白な手段は正規表現マッチング用のネイティブコードコンパイラを書くことではなく、インデックスを作ることだからだ。ただ、ここで言いたいのは、かつてはかなりの時間と専門性を要したこの種の技術的な作業が、今ではごく簡単にできるようになったということだ。そしてテキストインデックスを作りたいのであれば、ちょうど私は継続的かつ高速なテキスト取り込みに特化したBingの検索インデックスであるBitFunnelに携わっており、それはSIGIRでBest Paper Awardを受賞したのだが、だからマシン全体の高速なローカルインデックスを構築するなら試してみたい実験がいくつか思いつく(私が見てきたプロジェクトはコードディレクトリをインデックスする意図のものが多いようだが、私のマシンのパフォーマンスを本当に殺しているのは、codexが大量の生成ファイルを含む巨大な一時ディレクトリに対してripgrepを実行し、外したときにマシン全体を対象に拡大していくときなので、一部のプロジェクトのコードだけでなくディスク全体のインデックスが欲しい)。

もし私がAIラボで働いていて、Cerebrasチップやその他のアクセラレータ上で動作するSOTAモデルなど、トークン毎秒(tok/s)を大幅に引き上げ、結果として検索への負荷や需要を増大させるものにアクセスできる立場なら、既存のインデクサが十分に速いか、それとも自分でカスタムのものを作りたいかを実際に調査するだろう。BitFunnelのオープンソース版は「たかだか」バイトコードインタプリタと一つのJITしか含んでいないが、Bing版は複数のJITコンパイラを含んでいる。そのレベルの最適化を行うプロジェクトはかつては大事業だったが、「週末でできるよ」という言葉が、今ではこうした種類のプロジェクトの一部では実際に真実になっている。月200ドルのささやかなアカウントしか持っていない私にとっては、少し速くなったripgrepに既製のインデックスを組み合わせれば十分だと思うので、この高速取り込みのマシン全体インデックスのプロジェクトは「読者(AIラボで働いている人)への演習問題」として残しておいてもいいだろう。

最適化は安い

最適化のコストが劇的に下がったことは、2025年11月にまで遡って当てはまり、公開モデルでもおそらくそれ以前からそうだった(AIラボの人たちがアクセスできていたものでは、さらに前からそうだったのは間違いない)。GPT-5.1や5.2時代の例を挙げると、ゲームAIについての知識がまったくない状態でAzulのAIを作ってみたことがある。結果的にそれは、そのゲームで世界最強のAIとなり、しかもかなり大きな差をつけての最強だった。2番目に強いAIについて書かれた論文を読んだ限り、私のAIは「AI」的な部分でもおそらく少し優れていると思うが、勝敗を分けた主な要因は最適化だ。しかも、論文を読んでプロセスを比較した推定では、私の方がおそらく2桁ほど少ない時間しか費やしておらず、さらにクラスタではなくほぼラップトップだけで作業した(つまり実験を回したりパラメータチューニングをしたりするための帯域がはるかに少ない)にもかかわらずだ。例えば、もう一方のAIはシングルスレッドだが、私のAIはマルチスレッドだ。ネイティブコード版に加えて恐ろしく複雑な共有Wasmメモリ+JavaScript版もあり、さらに2つの異なるバージョンで2つの異なる探索アーキテクチャ(非常に小さく高速なネット用のminimaxと、より大きなネット用のMCTS)を採用しており、それぞれがまったく異なるマルチスレッドアルゴリズムを「必要とする」ため、これを手作業でやっていればかなり大掛かりな作業になっていたはずだ。しかも、自分でゲームAIのマルチスレッドアルゴリズムについて30分かけて調べる前に、LLM自身の(誤った)推論に基づいてマルチスレッドアルゴリズムを選ばせたことが何度かあったため、結局マルチスレッドアルゴリズムを何度も書き直す(codexに書き直させる)ことになった。

このようなマルチスレッドアルゴリズムをデバッグし検証するためには、アルゴリズムが非決定的であるにもかかわらずバグを再現できるように、デバッグログからのリプレイを実装するといった、やっておくべき定番の作業がいろいろある。それだけでも手作業でやれば数日から1週間はかかっただろうが、これはエージェントがループの中でごく簡単にこなせる類の作業だ(ログのリプレイを試させ、完全に再現できないたびに非決定性のためのロギングを挿入させればいい)。このような厄介な最適化を動くようにするのにかつて必要だった面倒な作業の多くがなくなったのだ。

これは他の多くの厄介な最適化にも当てはまる。CPUマイクロコードを書いたり、CPU検証をしたり、検索エンジンのインデックスの最適化に取り組んだりしてきた経験から、「ふむ、この最適化でパフォーマンスは2%上がるが、このトリッキーな最適化が正しく動くことを検証するのにN人日かかるな」と考え、それを動かすのにかける時間に見合うかどうかで実行するかどうかを判断してきたことが何度もある。今ではこのNが途方もない係数で下がった(ばらつきはあるが、人間の時間という観点ではしばしば1000倍、10000倍、1000000倍、トークンの従量課金でのコストと検索インデックスで使われていたJITのコンパイラを書いたBingのエンジニアのコストを比べた場合の金額ベースではおそらく1000倍程度)のだから、やる意味のあるこうした最適化の数は跳ね上がる。うまくいくかどうか分からない最適化についても同様だ。かつては、高速化するかどうか確信が持てない最適化を見て「パフォーマンスへの影響を推測できる程度にちゃんと測定できるまでに実装するのにM時間かかるな」と考えることがあった。今では、そうした最適化のうち試す意味があるものの数がはるかに増えている。

ゲームAIの話に戻ると、少なくとも私が試したAIでは、速度が2倍になるごとに約100 Elo強くなるようだ(チェスよりも大きいが、これは引き分けが非常に稀だからだと思う)。大規模なマシン上で、それ以外は同等のAIに対してマルチスレッドを追加するだけで一蹴できる。さらに、手作業では面倒すぎてほとんどの人がやらないような最適化を10〜20個積み重ねれば、強さの差は途方もないものになり、手書きのAIで追いつこうとするのはもはや現実的ではない3

ゲームAIのケースは、やりたい最適化の多くが実際に結果を変えてしまい、速度向上と結果の変化を合わせたときに実際に良い結果になるのか悪い結果になるのかを安価かつ簡単に判断する方法がないため、ほとんどのソフトウェアよりも少し面倒だ。そして、以前にも指摘したように、現在公開されているSOTAモデルは実験設計がかなり下手なので、最適化が良いかどうかを判断するために使うフレームワークは自分で用意しなければならなかったが、一度それが整えば、あとは他の最適化問題と変わらない。LLMの最適化に取り組んでいる人たちもこの種の問題に対処しなければならないのだろうと思うが、ほとんどの最適化問題はもっとずっと単純だ。

別の例を挙げると、パフォーマンス面接の準備の一環として、Jamie BrandonはAnthropicの現在公開されているパフォーマンス課題に取り組んだ。自分でやった後、Claudeに自分の続きからやらせたところ、はるかに良い結果を出した。Claudeが自分がやらなかったことを何やったのか見てみると、多くの最適化は自分にも思いついていたがまだ手が回っていなかったもので、「他のは、もしこれに何週間も取り組むのでなければ絶対に試さないようなクレイジーなものだった」と彼は言った4。彼は有能なパフォーマンスエンジニアであり、志望していたパフォーマンス職のオファーも得ているが、明確に定義された最適化問題では、まともなモデル相手に勝ち目はまったくない(私自身はその課題をやっていないが、 remotely comparableな時間制限であれば、私も勝ち目はないと思う)。

ワークロード固有の最適化

特定のワークロードにフィットさせた動的なカスタムソフトウェアは、あるクラスのワークロードに対するものではなく、非常に起こりそうな帰結だと思う。

これはかなり必然に思える。私の投稿に対する別の反応で、pgrustのMichael Malisも似たようなことを言っていた。

[pgrustの最適化についての議論] … これらの最適化を作るのは十分に簡単だと思うので、顧客のワークロードを見て、必要に応じて追加することができるだろう

何のフレームワークもセットアップもない状態で、この投稿を書き始める直前に、エージェントに私のripgrepクエリに対するワークロード固有の最適化をやらせた(ネイティブコードコンパイラへの切り替えではなく、一連のベンチマークに基づく汎用FREエンジンへの最適化だ)。起動するのに2分ほどしかかからなかった。最適化はあるクエリセットで実行され、その後で照合するための別のホールドアウトのクエリセットがある。まだ実行中だが、初期結果は有望に見える。1回の最適化パスを経ただけで、ワークロード最適化版はホールドアウトで標準のripgrepより2%高速であり、しかもまだ高速化が続いている。私のローカルでのripgrepの使い方にとって2%は大したことではないが、これが数分の作業で得られ、ここでの最適化がこの段落を書き始めたときに開始されてまだ改善し続けていることを考えれば、この2%の勝利は十分にありがたい(なお、これはネイティブコードコンパイラと組み合わせたものではなく、適切に組み合わせれば全体ではより大きな勝利になる)。そして思い出してほしいが、これはFRE正規表現エンジンを活用したものだ5。FREはホールドアウトのベンチマークではRustの正規表現エンジンより大幅に遅く、正規表現のワークロードについて何も知らない私と、誘導なしのオープンエンドな自己改善ループを行うには実験設計が十分に得意ではないSOTA LLMという組み合わせでは、ホールドアウトでのパフォーマンスを改善する良い方法がなく、改善が停滞していた。しかし、自分自身のワークロードでのパフォーマンスが気になるのであれば、データは十分にあり、しかも常に増え続けている。Marc Brookerが上で指摘したように、古いデータにないレジームチェンジがあった場合の過学習などには注意する必要があるが、それでも以前よりは良い状況にある。

より一般的なケースで考えれば、AmazonのMarc Brookerやpgrustに取り組むMichael Malisのような立場なら、これを単発で終わらせるのではなく、顧客と協力して彼らのデータを使って最適化するパイロットプログラムを実施し、それを顧客全体にスケールさせる方法を考えるのが理にかなっている。私はそれが時間の最良の使い方になるような会社で働いているわけではない6が、より大きなスケールを持つ企業にとってこれが来るのが見えているのはかなり驚くべきことだし、個人のワークフローでこれらの実験を走らせるのに数分しかかからないことを考えれば、個人プロジェクトでこの種のことに手を出すのは十分に理にかなっている。

Jamie Brandon、Michael Malis、andrea(@s__video)、Artyom Bologov、Max Bittkerの各氏にコメント/修正/議論の協力を感謝する。

追伸:前回2つの投稿でも述べたように、コーディングエージェントによって、実験を実行して自分の好奇心を満たすのに十分な結果を見るまでの時間は大幅に短縮された一方で、結果を本当に厳密にするのにかかる時間は変わらないかむしろ増えているため、以前と同じような書き方をしていては、自分の持つ帯域に対して実行できる実験の数が非常に少なくなってしまう。その結果、これらの実験はただ実行して数人の友人と結果を共有するだけになっていた。実験として、こうした実験が何年も数人の友人にしか知られないままになる代わりに、非常に速く厳密でない形で書き上げてみようとしている。前回の投稿と同様、今回の投稿も仕上げ作業を含めて30分で書き上げることを目標にしたが、計測はしなかったものの、おそらく少しオーバーしたと思う。

こうしても、書き上げるのにかかる時間は依然として長いため、最近の結果の共有が遅れ気味になっている。ただ、(今のところ?)LLMに投稿を書かせる方式に切り替える気はないし、データを整理してこのような投稿を書く時間を現実的に30分未満にまで短縮できるとも思えない。この投稿の長さだけでも、これをタイプするのに、何を書くか立ち止まって考える時間を含めて20〜30分はかかるはずだし、データを見たときに何かがおかしいと感じて、修正が必要な問題がないかより詳しく調べる必要が生じることもある(今回はそれが何度か起きたし、これ以上時間をかけなかったのだから、気づいていない他のデータの問題もあるだろうと思う)。

いずれにせよ、このような速い(そして確実により間違いの多い)書き方について意見があれば、ぜひ聞かせてほしい(X Bsky Mastodon)。

付録:ソフトウェアが遅い理由はもうない

私は以前から、Xの開発者はダメで恥じるべきだという一般的な見方には強く反対する立場を明確にしてきた。なぜならプログラミングの専門性には実にさまざまな種類があるからであり、ほとんどのプログラマがパフォーマンスの専門性を持っていないというだけでなく、おそらくそれを身につけること自体が理にかなっていない(ビジネスが重視すること、雇用市場の状況などから見て)からだ。だから、ほとんどのプロジェクトのパフォーマンスが、パフォーマンスの専門家ができることと比べて非常に劣るのは当然のことだ。プログラムがどれだけ速くなりうるかと実際にどれだけ速いかの間に広がるギャップを見て、パフォーマンスの専門家がそれを馬鹿げていると思う気持ちは理解できる。そこに不条理があること自体は否定しない。だが、UIがどれだけ良くなりうるかと自分が(手作業で)作れるUIがどれだけ良いものになるかのギャップを考えてみても、それが同じくらい不条理に見えないとは思えない。しかし、ほとんどの人がまともなパフォーマンス改善のやり方を学ぶのに時間を費やすのが理にかなっていないのと同じ理由で、自分が優れたUI、あるいはまともなUIですら作れるようになるために時間を費やすことが本当に理にかなっているとも思えない。

上記の例で言えば、Jamie BrandonはAnthropicからオファーを得ており、あなたがOpenAIでもない限り彼を雇うことはおそらくできないが、限定された最適化問題で彼に勝てるコーディングエージェントを使うことならできる。エージェントは彼のような判断力を持っておらず、オープンエンドな問題ではより悪い結果になる(最適化された正規表現エンジンを作ろうとして、ただ過学習しないようにとだけ指示したとき、ホールドアウトのベンチマークで最良の正規表現エンジンより1桁以上も遅かったことを思い出してほしい。ただ、ホールドアウトで成績が悪いことをエージェントに伝えた後は、正規表現エンジンのパフォーマンスを十分に高速化して概ね2番手グループの正規表現エンジンに匹敵するパフォーマンスにまで持っていったことも思い出してほしい。これは今日のほとんどのコードにおける一般的なパフォーマンス最適化のレベルと比べれば依然として極めて優秀だ)が、さまざまな問題でまずまずのパフォーマンスを達成するには十分すぎるほどだ。この投稿では主にバックエンドのパフォーマンス問題について論じてきたが、LCPやCLSのような一連の指標を下げたいのであれば、エージェントはフロントエンドのパフォーマンスでも劣っているようには見えない。実際、最近投稿で使っているインタラクティブなプロットを挿入した後、クライアントサイドのパフォーマンス数値が悪化していることに気づいたので、LLMに週間クォータの1%を使ってそれらを最適化させたところ、数値は再び良好に戻った。これは非常にシンプルなサイトだが、何百万人ものユーザーに届くかなり複雑なアプリでも人々はこうした最適化を行っており、そこでもうまくいっている。ただし、もう少し多くのトークンはかかるが。

私は今でも、ソフトウェアのパフォーマンスが悪いからといって誰かがダメで恥じるべきだとは思わない。ただ、パフォーマンスについて何も知らない人でも、LLMの(パフォーマンス問題に限らず、一般的に)まずまずの使い手であれば、概ねまともなパフォーマンスを持つソフトウェアを作れるはずだと思う。LLMにただ最適化しろと指示するだけだと、しばしば非常にまずい間違ったことをいろいろとしでかすので、それを捕まえる必要があるが、それはそもそもLLMを効果的に使うこと一般に当てはまることなので、まともなパフォーマンスを得ることはもはや専門的なスキルではなくなったのだ。

付録:codexはどのようにripgrepを実行しているか

ここで、私のマシン上でのripgrepクエリの分布について少し情報を示しておこう。これが他のどこで起きていることを代表していると主張するつもりはまったくない。検索されるパターンの長さの分布は、私が予想していたよりもはるかに長いパターンが多い。p50は55 Unicodeコードポイント(簡単のため、ここでは単に文字と呼ぶことにする)で、これはすでに私が手作業でgrepするものよりも長く、p90は119だ!

また、正規表現における alternation(選択肢)の数も見てみることができるが、これも再び私が手作業でやるものよりはるかに複雑だ。

別の見方として、これらがどのように相関しているかを見てみよう。正規表現が長くなるにつれて選択肢の数は増えるのだろうか? 答えはイエスだ。

そもそも、これらの非常に長い正規表現とは何なのだろうか? 見てみると、最も長いものの大半は関数名やテスト名に対する長い選択肢の列挙で、例えば次の正規表現のように、FREの開発に関連しているように見えるものだ。

  fn (hot_byte_compiler_is_generic_only_and_anonymous_count_uses_auto_count|
  one_pattern_count_spans_uses_the_retained_complete_span_session|
  formal_compact_state_byte_visitors_coexist_with_native_count|
  fixed_boundary_record_visit_matches_line_relative_reference_and_is_atomic|
  unbounded_languages_refuse_finite_extraction_before_allocation|
  formal_single_raw_span_sweep_preflight|
  assert_exact_fixture_uses_formal_large_continuation_sweep|
  url_only_compile_identity_binds_language_and_owner_mode|
  url_only_compile_exact_limits_and_runtime_refusals_close|
  url_only_compile_post_plan_allocation_faults_close|
  url_only_owner_discriminator_is_stable_and_precharged|
  url_only_compile_owner_is_strategy_and_operation_scoped|
  formal_rebar_url_owner_is_compile_only_and_matches_oracle|
  formal_rebar_url_exact_fixture_uses_certified_execution|
  formal_fixed_schema_materialization_matches_both_record_oracles_and_controls|
  formal_single_count_selects_compact_state_byte_complete_bound_visitors|
  authenticated_bound_line_total_lf_free_domain_opportunity_exceeds_five_percent|
  prepared_absolute_onepass_fuses_slots_and_preserves_pre_source_fallback|
  authenticated_word_boundary_russian_compact_lowering_public_canary|
  ordered_nfa_x86_epsilon_edges_bypass_the_assertion_call|
  ordered_nfa_aarch64_epsilon_edges_bypass_the_assertion_call|
  ordered_edge_dispatch_v2_is_target_neutral_deterministic_and_relocation_free|
  ordered_edge_dispatch_v2_copies_canonical_tables_and_cap_falls_back_to_v1|
  ordered_nfa_v3_composes_terminal_range_and_dispatch_without_data_relocations|
  ordered_nfa_x86_terminal_range_emits_authenticated_reverse_scan|
  ordered_nfa_aarch64_terminal_range_emits_authenticated_reverse_scan|
  ordered_nfa_x86_boundary_assertion_cache_is_lazy_and_boundary_scoped|
  ordered_nfa_aarch64_caches_repeated_assertions_once_per_boundary|
  boundary_assertion_cache_requires_dense_exact_kind_reuse|
  boundary_assertion_cache_selection_is_compiler_only_and_deterministic)

ただ、中には次のような、ちょっと面白い数値的な構成のものもある。

:(13[0-9]|14[0-9]|15[0-9]|16[0-9]|17[0-9]|18[0-9]|19[0-9]|20[0-9]|21[0-9]|22[0-9]|23[0-9]|24[0-9]|25[0-9]|26[0-9]|27[0-9]|28[0-9]|29[0-9]|30[0-9]|31[0-9]|32[0-9]|33[0-9]|34[0-9]|35[0-9]|36[0-9]|37[0-9]|38[0-9]|39[0-9]|40[0-9]|41[0-9]|42[0-9]|43[0-9]|44[0-9]|45[0-9]|46[0-9]|47[0-9]|48[0-9]|49[0-9]|50[0-9]|51[0-9]|52[0-9]|53[0-9]|54[0-9]|55[0-9]|56[0-9]|57[0-9]|58[0-9]|59[0-9]|60[0-9]|61[0-9]|62[0-9]|63[0-9]|64[0-9]|65[0-9]|66[0-9]|67[0-9]|68[0-9]|69[0-9]|70[0-9]|71[0-9]|72[0-9]|73[0-9]|74[0-9]|75[0-9]|76[0-9]|77[0-9]|78[0-9]|79[0-9]|80[0-9]|81[0-9]|82[0-9]|83[0-9]|84[0-9]|85[0-9]|86[0-9]|87[0-9]|88[0-9]|89[0-9]|90[0-9]|91[0-9]|92[0-9]|93[0-9]|94[0-9]|95[0-9]|96[0-9]|97[0-9]|98[0-9]|99[0-9])[0-9]:

これは:(?:1[3-9]|[2-9][0-9])[0-9]{2}:と等価だ(これを元の入力に対してripgrepで実行するとパフォーマンスはほぼ同じで、短い方の正規表現の方が実際のクエリデータでは技術的にはわずかに速いが、その差はごくわずかだ)。そのときのパイプライン全体は次のようなものだった。

cargo clippy … | rg 'crates/fre-aot-regex/src/module.rs:' | rg NUMBER_REGEX | head -250

これは人間がやるには奇妙なことに思えるかもしれないが、エージェントはこういうことを常に行っているようだ。

別の話題として、ripgrepクエリがどれくらい時間がかかったかを見てみると、かなり多くの遅いクエリがある。例えばp99はほぼ1分だ! そしてp999はほぼ10分だ! さらにこの期間(1台のラップトップで約1か月間。クエリや分布は、私がエージェントを動かしているAWSホストなどでは異なる可能性が高いが、確認はしていない)における最大のクエリは2時間に迫っている!

コマンドラインオプションに関しては、次のような状況が見られる。おそらく驚くことではないが、codexはしばしば行番号を欲しがり、何らかの理由でごくたまにPCRE2正規表現を使っている。

これらについてはプロットや表は載せないが、他に注意すべき点として、検索されるパターンには局所性がかなり低い(パターンの約94%は一度しか出現していない)ということがある。これは多くのクエリが非常に長かったことを考えればある程度納得がいく。しかし、検索されるファイルにはかなり高い局所性があり、一度検索されたファイルは近いうちに再び検索される可能性が比較的高い。これは(十分に小さなファイルであれば)メモリ内で検索される可能性が高いことを示している。

また、クエリの99%は正規表現クエリで(1%は正規表現ではない文字列検索だった)、検索クエリの99.9%はASCIIのみだったが、検索されたファイルという観点では約45%がASCIIのみで55%がUnicodeを含んでおり、Unicodeの割合は私が予想していたよりも高かった。

前回の投稿のドラフトに対して、Peter Geogheganはこう指摘していた

正規表現の実装がより少ない機能をサポートすることで高速になることもありうる。バックリファレンスなどをサポートしていない実装もある、といった具合だ。

これはここでも当てはまる。ここで行われたワークロード固有の最適化はかなり表層的なものだった。というのも、codexに短い指示を与えてあとは好きにやらせただけだからだ(これは一般的に言ってcodexの最も効果的な使い方ではない)。しかし、より詳細な計画があれば、私のクエリの一般的なユースケースをサポートする、より焦点を絞った最適化によって、より大きな成果が期待できるだろう。


  1. ただし、その投稿以前にも論じたように、SOTAモデルのベンチマークや実験設計のスキルは、人間(あるいはスキル)がエージェントのためにベンチマーク環境をセットアップしない限り、一般的なケースでこれを行うのに十分ではない。[戻る]
  2. 以前のベンチマークから分かるように、コンパイラを実行する時間があっても、ネイティブコードにコンパイルされたバージョンの方がRustの正規表現クレートより遅いケースがたくさんある。これがなぜかを調べると、これらはRustの正規表現クレートが何らかのアルゴリズム上の最適化を持ち、FREのネイティブコードコンパイラが単純なフォールバックに頼っているような、より複雑なクエリである傾向がある(FREを作ったエージェントは、「通常の」正規表現エンジンに費やした時間よりも、ネイティブコードコンパイラに費やした時間の方がはるかに少なかった)。[戻る]
  3. 世界トップクラスの囲碁やチェスエンジンを書いた人のような、本当にAIの専門性を持つ人が手書きしたAIなら、「AI」的な部分の出来が、私のようなAIについて何も知らない人が作ったAIよりも優れていることで、私のAIに勝てることは間違いないと思うが、専門性のレベルが remotely 同程度であれば、費やした時間の長さが同じならLLMが書いたバージョンが支配的になるだろう。[戻る]
  4. 彼が残した作業を引き継がせたエージェントの結果を比較するのは不公平だという議論も成り立つ。彼の作業は出発点であり、エージェントが単独でやるよりもはるかに良い結果を出せる可能性があるからだ。そこで、新しいタスクをそのままエージェントに与えてみたところ、彼の作業を再利用したエージェントが得たスコアと非常に似たスコアになった(別のエージェントによる簡単なチェックでも不正の証拠は見つからなかった)。[戻る]
  5. おそらく、エージェントにripgrepのフォークを直接修正させていればパフォーマンスはより良くなっていただろうが、私のクエリに関してFREの過学習問題もこれで解決できるかどうかが気になっていた。[戻る]
  6. しばらく前に、サインアップフローのページサイズを50MBから5MBに削減したところ、収益のA/Bテストでは収益が約0.5%増加したように見えた。一般的に、私はまずこういった単純で簡単な勝利を先に取るのが大好きで、ここでカスタムコンパイラを構築したり他の高度に専門的な技術的作業を行ったりして得られるものよりも、ROIが高い勝利はおそらくたくさんある。[戻る]

この記事は「muse-spark-1.2-contributor」を使用して翻訳されました。

コメント