- この記事は、純粋な Python コードを事前コンパイル(AOT)してクロスプラットフォーム実行ファイルに変換しようとする試みと設計を、事例中心に説明している
- 中核となるアイデアは、JIT を新たに作ったり C++ に全面書き換えしたりするのではなく、シンボリックトレーシング→IR→C++ コード生成→マルチターゲットコンパイルのパイプラインで最適化カーネルを生成する方式である
- PEP 484 の型アノテーションで型伝播のシードを与え、AI コード生成で数百個のC++ オペレーターを自動実装し、Numpy・OpenCV・PyTorch など幅広いライブラリ呼び出しをカバーする
- 同じ Python 関数に対して多様な実装経路を大量生成・配布し、実測テレメトリで最速のバリアントを選ぶ経験的性能最適化戦略を採用している
- 目標は、コンテナに依存しない小さく高速で移植可能なバイナリを提供し、サーバー・デスクトップ・モバイル・Web までどこでも実行できる配布単位にすること
Foreword
- Python の単純さと生産性は長所だが、高負荷ワークロードでは性能と移植性に限界がある
- ゲスト筆者 Yusuf Olokoba による本稿は、元の Python を維持したまま、高速で持ち運びやすい実行ファイルを作るコンパイラ設計を紹介するもの
- JIT の追加や全面的な C++ リライトなしにパイプラインを構成してカーネル最適化を達成しようとするアプローチである
Introduction
- 目標は、修正なしの Pythonを完全 AOTでコンパイルし、インタープリターなしで動作し、C/C++ に近い速度で、あらゆるプラットフォームで実行できるようにすること
- 既存の試み(Jython、RustPython、Numba、PyTorch、Mojo など)とは異なり、言語やランタイムの置き換えではなく、コード変換とカーネル生成を選んでいる
- これらのコンパイル済み Python 関数は、すでに毎月数千台のデバイスで使われている
Containers Are the Wrong Way to Distribute AI
- 実運用での配布では、コンテナは過大なペイロード(インタープリター・パッケージ・OS スナップショット)を伴い、起動遅延や移植性の制約を引き起こす
- 代替案は、モデルだけを含む自己完結型の実行ファイルであり、より小さく高速な起動とサーバー・デスクトップ・モバイル・Web全体での実行可能性を提供する
- 要点は、配布単位を OS スナップショットではなく自己実行バイナリへ切り替える戦略である
Arm64, Apple, and Unity: How It All Began
- Apple のarm64 移行当時、Unity が IL2CPP により CIL を C++ に変換してあらゆるターゲット向けにコンパイル可能にした事例をベンチマークとした
- 同じ発想をPython に適用し、どこでも動かせるコード経路を確保しようというビジョンを打ち立てた
Sketching Out a Python Compiler
- 上位設計は、Python 入力 → シンボリックトレース(IR) → C++ 生成 → マルチターゲットコンパイルの段階で構成される
- IR から直接オブジェクトコードへ進まず、C++ を中間生成物として選んだ理由は、CUDA・MLX・TensorRT・AMX などの高速化経路を最大限活用するためである
- 目標は、ハードウェアごとの最適経路を差し込みやすい拡張性の高い設計を確保すること
Building a Symbolic Tracer for Python
- 初期の PyTorch FX ベースのトレーシングは、実行が必要であることとPyTorch 演算に限定されることから限界があった
- そこで代わりに、AST 解析ベースのシンボリックトレーサーを構築し、制御フローや呼び出し解釈を IR に変換する
- 現在のトレーサーは、静的解析、部分評価、サンドボックスベースのライブ値観測などの機能を提供する
Lowering to C++ via Type Propagation
- Python の動的型付けと C++ の静的型付けの橋渡しをするために、型伝播を用いる
- 入力引数の型が与えられれば、中間変数の型は演算子定義に基づいて決定的に推論できる
- 各 Python 演算を対応する C++ 実装へマッピングしながら、関数全体に型を伝播させる
Seeding the Type Propagation Process
- 型伝播のシードとしてPEP 484 型アノテーションを使う
- これは元コードを変更しない原則とは矛盾するが、簡潔なインターフェースと互換性のために許容可能な妥協と判断している
- また、関数シグネチャの型数を制限するなどの制約を設け、シンプルな利用インターフェースを保証している
Building a Library of C++ Operators
- すべての関数を C++ で直接実装するのではなく、トレース不可能なリーフ演算だけを手動または自動で実装すればよい
- 多くの Python コードは少数の基本演算の組み合わせで構成されるため、カバーすべきオペレーター集合は比較的小さい
- LLM ベースのコード生成と制約・テスト・条件付きコンパイル基盤により、Numpy・OpenCV・PyTorchなど数百の関数実装を自動化している
Performance Optimization via Exhaustive Search
- 性能最適化は常に経験的だという教訓に従い、複数の実装バリアントをすべて生成して実測比較で最適なものを選ぶ
- 例として Apple Silicon では、リサイズだけでもAccelerate、vImage、Core Image、Metalなど複数の経路を生成し、同一機能の複数バイナリを配布する
- 細かなテレメトリで経路ごとのレイテンシを収集し、統計モデルによって最速のバリアントを予測・選択する
- その効果は、ユーザー視点では時間が経つほど自動的に速くなる実行体験として現れる
Designing a User Interface for the Compiler
- 開発者体験を学習コストほぼゼロに近づけるため、PEP 318 デコレーター
@compile をインターフェースとして採用している
- CLI はこのデコレーターをエントリーポイントとして、依存コードグラフを探索してコンパイルする
- デコレーター引数としてtag・description・sandbox・metadataを受け取り、**環境再現・バックエンド指定(ONNXRuntime、TensorRT、CoreML、IREE、QNN など)**をサポートする
Closing Thoughts
- 例外・ラムダ・再帰・クラスなどは部分対応または未対応であり、とくに複合型・高階型では型伝播の拡張が必要である
- デバッグ体験については、最適化コンパイルによりシンボル情報が減るため、追跡の難しさが課題となる
- C++20 の
std::span、concepts、coroutines が中核基盤であり、C++23 の std::generator、<stdfloat>、<stacktrace> はストリーミング・half/bfloat16・例外追跡に貢献する予定である
- 最終目標は、コンテナなしの小さく高速で安全な実行ファイルにより、埋め込みや検知などの AI ワークロードをどこでも実行できる配布単位を確立すること
1件のコメント
APEみたいなものかと思ったけど、そうではないですね。