世界を動かす技術を、日本語で。

プログラム移植におけるファジングの非合理的な効果

概要

  • LLM による自動コード生成・ポーティングの進展とその影響
  • API設計・メンテナンス の変革と将来の展望
  • TensorFlow のメンテナンス経験から見る技術的負債の課題
  • CからRust へのポーティングをLLMとプロパティテストで効率化
  • プロパティテストや fuzzテスト の有効性と課題

LLMによるコード自動生成とメンテナンスの未来

  • LLM が大量のコードを生成する時代の到来予測
  • 人間中心から コンピュータ中心 へのコード作成パラダイムシフト
  • APIの互換性問題 や不整合の解決が容易化
  • LLM活用による APIの抜本的刷新 や大規模リファクタリングの可能性
  • 従来は困難だった 異言語間ポーティング やAPI変更の自動化
  • ユーザーには LLMプロンプト を提供し、コード自動移行を実現
  • リファクタリングや大規模修正の意思決定プロセスの変化

TensorFlowのメンテナンス経験と技術的負債

  • TensorFlow における設計上の問題と技術的負債の蓄積
  • キャリア上の動機による 機能追加 とその副作用
  • Pythonコードの肥大化 と複雑化による保守性・性能低下
  • 優秀なエンジニアの流出とメンテナンスの困難化
  • C++層への段階的移植 による改善案とその実現困難性
  • 他プロジェクト(例: libexpat)でも同様のリファクタ難易度

LLMエージェントの実用性とテスト駆動開発

  • Claude CodeAider などのエージェントによる問題解決力
  • API変更後の 自動修正 やテスト通過までの自律的対応
  • 強いテストケースがある場合の LLMの精度向上
  • 入力・出力の一致を保証することで リファクタリングの信頼性 を担保
  • 既存プロジェクトの大規模変更時に テスト自動化 が有効

CからRustへのポーティングとプロパティテスト

  • c2rust によるC→Rustの機械的変換とLLM活用の新潮流
  • Syzygy 論文による完全ライブラリ移植事例とその成果・課題
  • 性能劣化やAPI互換性の難しさ( Hyrum's Law
  • CとRustの出力を ランダムに比較 するプロパティテストの提案
  • プロパティテスト の有効性と限界(入力生成や汎化の難しさ)
  • C→Rust移植時の理想的なテスト条件: 同一入力で同一出力

実際のポーティング試行とその教訓

  • 初回試行: ユニットテスト依存 によるバグの顕在化とスケール困難
  • 改善策: 各モジュールごとにRust移植&fuzzテスト の実施
    • LLMによるバグ修正の自動化
    • 手作業の大幅削減と再現性向上への課題
  • fuzzテスト とプロパティテストの組み合わせによる移植精度向上
  • 今後の課題:完全自動化とプロセスの汎用化

LLMが変えるコード生成とメンテナンス

  • LLM の進化により、コード生成量が人間を上回る未来の到来
  • API設計 やリファクタリングが、LLM主導で容易化する可能性
  • ライブラリの大規模移植 やAPI変更も、LLMとプロンプトで自動化
  • 保守性向上のための 大規模内部改善 もコスト低減
  • 意思決定プロセス の変革(プロンプト設計重視)

技術的負債とメンテナンスの現実

  • TensorFlow など大規模OSSの技術的負債蓄積
  • 機能追加優先 による設計の複雑化と保守性低下
  • 段階的リファクタリング の困難とリソース不足
  • 他OSS でも同様の問題が発生
  • LLM活用 でリファクタのコスト削減可能性

LLMエージェントの強みと限界

  • 具体的なテストケース がある場合、LLMの自律的修正能力が発揮
  • テスト自動化 によるバグ検出と修正の高速化
  • プロパティテスト との組み合わせで信頼性向上
  • 入力・出力の一致で 移植の正当性 を担保
  • 完全自動化 にはさらなる工夫が必要

C→Rust移植とプロパティテスト戦略

  • c2rustSyzygy などの既存手法とLLMとの比較
  • fuzzテスト +プロパティテストでC/Rustの動作一致を検証
  • 入力網羅性 やプロパティ設計の難しさ
  • LLMによるテスト生成支援 の有効性
  • 移植プロセスの自動化 と再現性向上への展望

今後の展望と課題

  • LLM を活用したコード移植・リファクタリングの完全自動化
  • プロパティテスト やfuzzテストのさらなる活用
  • API設計・保守 の抜本的変革
  • 技術的負債 解消のコスト低減
  • OSS・商用プロジェクト双方での LLM活用拡大

Hackerたちの意見

参考までに:リンク「セッションログはここで確認できます。」は壊れてるよ。

修正したよ、ありがとう!

ほとんどのコードは微妙なロジックパスを表現してないよね。もし百万の入力が正しくソートされてるかテストするなら、たぶんソーターは正しく実装されてると思う。これがZopfliのソーターに関して言ってるのか、一般的なソートについてなのかは分からないけど、Timsortに微妙なソートバグがあるって聞いたことがあるよ。

シェアしてくれてありがとう、知らなかった!確かに、これはポーティングの際に心配になる微妙なケースだね。ファジングでは、巨大な入力や特別なリストの構成が必要なバグを発見するのは難しいと思う。実際には、ほとんどの場合LLMが正しいコードを書いてくれるから、問題ないと思うし、もし間違ってもすぐに直せるバグが多い。ただ、LLMが微妙なバグを引き起こすと、対処がさらに難しくなるよね…。

ジェネレーティブテストには2つの主な問題がある:- 入力データの生成(プログラムの動作を十分に探索して、テストが完全な正しさの良い代理である自信を持つにはどうするか) - 正しさの表現(任意の入力に対してプログラムが正しいかどうかをどう表現するか) プログラムをポートする時には、組み込まれた正しさの表現がある:ポートは元のプログラムと全く同じように動作するべきだ。これがテストプロセスを大いに簡素化するんだ。

何度かコードのポートに関わったことがある。最終的に、「動かなかった」「古いシステムでも動かなかった」というたくさんのバグ報告が来る時期が来るんだ。これは、正しくポートしたけど、古いシステムも正しくなかったってこと。新しいシステムが徹底的なテストの予算を持つまで、その状況でテストしてなかっただけなんだ。(普通は、古いシステムで一度は動いてたけど、他のアップデートで壊れたんだよね)

Matlabのライブラリ全体をOctave(または好きなオープンソース言語)にポートするのに同じアプローチを使わないのはなぜ? Matlabのマニュアルは公開されてるから、クリーンルームリバースエンジニアリングになるよ。(しかも、実装されている内容の基礎的な定義に関する適切な参考文献がマニュアルページに載ってることが多い)

LLMは、過去には考えもしなかったような大規模な更新を行う扉を開いてくれる。ライブラリをある言語から別の言語にポートできるし、問題を修正するためにAPIを変更できる。下流のユーザーには、コードを自分で書き直す代わりに新しいバージョンに自動的に移行するためのLLMプロンプトを提供できる。大規模な内部リファクタリングもできる。過去には、こういったタスクはシニアエンジニアがプロジェクトで最後の手段として拒否するのが普通だった。顧客を壊すのはほとんど利益にならないし、「メンテナンスモード」のプロジェクトでリファクタリングを正当化するのは難しい。 > でも、もし適切なプロンプトを見つけてLLMに作業をさせることが重要なら、私たちの意思決定プロセスが変わるかもしれない。ライブラリの利用者が理解できるように破壊的変更を十分に詳細に文書化することと、「自動的に移行するためのLLMプロンプトを書くこと」にはあまり違いがないと思うけど、それがメンテナが変更を伝えるために必要なら、いいよ!ただ、「このライブラリの変更に基づいてすでに訓練されたLLMを使って、コードベースを提供すれば修正するから。PS:ごめん、ドキュメントはなし。」みたいにならない限りは。

ドキュメントとプロンプトの間には大きな違いがあるよね。具体的な例を挙げると、"あなたの研究コードをHugging Faceで推論用に公開して"ってリクエストが来るんだけど、そのリンク先のガイドはGitベースのリポジトリやコラボ機能、TensorBoardの統合についてのマーケティングコピーが80%占めてる。実際の実装の詳細は散らばってる感じ。プロンプトの方がずっとコンパクトだよね。違いは、プロンプトなら30秒で読んで"うん、これは合理的"とか"いや、これを変えたくない"って判断できるけど、ドキュメントだと自分の特定のシナリオに合う狭い範囲を逆算しないといけない。リクエストを出してる人が一番何をしたいか明確にわかってるから、彼らの知識を詰め込んでほしいな。ドキュメントは"今できることは全部ここにあるよ!"って言ってるけど、プロンプトは"必要な具体的な情報はこれだよ"って言ってる。プロンプトは今や共通の社会的慣習になってるし、みんながどんな情報を提供すべきか大体わかってる。具体的に、あいまいにしちゃダメ。誰かに"プロンプトを書いて"って頼むと、"Xをサポートして"って頼むのとは全然違うマインドセットになる。今はみんなプロンプトを書く経験があるから、その経験を活かして協力的な成果を得たいな。労働の分担ってやつだよね。君が初稿を書いて、俺が自分のコードベースに関する特別な知識で編集して、それを適用する。そうすれば、メンテナに全部押し付けるんじゃなくて、みんなで作業を分担できる。

同じアイデアが同じ時期に浮かぶのがすごいと思う。例えば、私はテスト生成に取り組んでいて、同じ道を辿った。最初は「このコードのバグを見つけて、それを示すテストを実装して。」とプロンプトを出してバグを探そうとしたけど、あまり進展しなかった。それで、あなたと同じようにプロパティ(不変)テストに切り替えたけど、私の場合はAIに「コードベース全体に基づいてプロパティテストを作って。」と頼んで、状態を持つオブジェクトに対してランダムなアクションをいくつか行い、プロパティテストを何度も実行してる。最初は全てを自動化したいと思ってたけど、時間が経つにつれて、最適なのは人間が10%、AIが90%の作業だと気づいた。もう一つ探求しているアイデアはAI + ミューテーションテスト(https://en.wikipedia.org/wiki/Mutation_testing)。これがAIにフルカバレッジの生成を助けるはず。

あまり探求されていないアプローチは、アプリの人間の使用データを収集すること(本番環境や内部テスターから)で、それをジェネレーティブ入力にフィードすることだよ。

人間がプロパティテストを選んだAIのコードベースの方が、自分がプロパティテストを選んだ人間のコードベースよりもずっと信頼できると思う。テストは実行可能な仕様だから、LLMに任せるべき最後のことだよ。

TensorFlowが今はあまり使われてないって聞いて、ちょっと嬉しかったな(Google以外は)。Googleトレンドをチェックしなきゃと思ったよ。https://trends.google.com/trends/explore?date=all&q=%2Fg%2F1... 数年前にTensorFlowを使い始めて、今はPyTorchに切り替えた。MLがTensorFlowからPyTorchへの移行をもっとスムーズにしてくれるといいな。大企業だけがオープンソースコミュニティを食い物にするんじゃなくて、ね。

GoogleはJAXに移行したんだ。PyTorchよりも好む人が多いって知ってるよ。

面白い!でも、ここには期待と実際に達成されたことのギャップがあるね。ブログの最初の方で、著者は"c2rustはCコードをRustに機械的に翻訳できるが、結果は意図的に'Rust構文のC'だ"って言ってる。投稿の流れからすると、LLMがもっと良い結果を出せるように思える。でも後で、彼らの最終的なLLMアプローチは"非常に'Cライク'なRustコードを生成する"って言ってる。なぜなら、"ポーティングする各シンボルに対して同じunsafe Cインターフェースを使っているから"。つまり、c2rustとほぼ同じ結果を得たけど、遅くて信頼性の低いプロセスだったってことだね。確かに、著者が言うように、"最終結果にはエンドツーエンドのファジングテストと各シンボルのテストがあるから、コードを自信を持って'ラストファイ'するのがずっと簡単になった"。でも、実際のポーティングにはc2rustを使って、別にLLMを使ってファジングテストを書くこともできたはず。アプローチを批判してるわけじゃないけど、LLMベースのコードポーティングには大きな可能性があるのは明らかだね。投稿で言及されていた以前のファジングベースでないClaudeポートを見てみたけど、イディオマティックなRustコードのように読めた。もしそれが(著者によると)微妙にバグがあってなければ、完璧な概念実証になっただろう。もしかしたら、機械的翻訳と比較して利点を保ちながらバグを取り除くためにファジングを使う方法があるかもしれない。でも、著者の特定のファジングアプローチは、バグと利点の両方を取り除いてしまったようだ。それでも、今後の作業の基盤としては良いと思う。

それは中間的な感じだね。ClaudeポートよりはCっぽいけど、c2rustよりはRustっぽい。どれくらいCっぽくするかは、ポートの細かさやLLMへのプロンプトの仕方によるよ。関数の内部や内部シンボルに関しては、LLMはもっと慣用的な構文や構造を使ってもいいんだ。でも、目的はファズテストの効果を試すことだったから、シンボルの翻訳にLLMを使うのは実装の細かい部分って感じかな。初期の翻訳にc2rustを使うのも悪くないアイデアだけど、LLMがこの部分で苦労しているとは思わなかったし、こっちの方が柔軟性もあるよ。c2rustも簡単な関数でつまずいてたから、あんまり深くは追求しなかったしね。もちろん、外部シンボルに関してはC APIに制約されるから、どれだけ自由度があるかはプロジェクト次第だね。最初からLLMにもっと慣用的なコードを生成させることも考えられるけど、それはシンボルごとのインクリメンタルな翻訳と合わせるのが難しいかもしれないね。

この一般的なアプローチを使って、既存のGolang実装からClickHouse特有のcityHash64をJSにポーティングしたよ。https://github.com/maxjustus/node-ch-city/blob/main/ch64.js。純粋な関数をポーティングする時は特にうまくいくと思う。

ウィグナーの数学に関する論文は、そういうテキストの命名規則を生み出したよね。