
2025/12/24 7:53
**コンパイラにおける静的割り当て**
RSS: https://news.ycombinator.com/rss
要約▶
Japanese Translation:
(combining missing points and tightening language):**
## Summary TigerBeetle の「静的割り当て」設計―起動時にコマンドライン引数に基づいてすべてのメモリを確保し、再割り当てを行わない――は RAM 使用量を制限します。システムは 1 MiB 以下の有限なメッセージのみを処理し、データはディスクに保存されます。不安定なネットワークでは、処理できないメッセージを永続的にキューに保持する代わりに単純に破棄します。この構成型アプローチにより、複数の有限 I/O サブシステム(例:routing.zig)を追加オーバーヘッドなしで組み合わせることができます。 記事はコンパイラも同様の分割アーキテクチャを採用できると主張します。大きな不変「出力領域」を持ち、O(1) メモリで処理される小さなストリーム処理コアが存在する構成です。各コンパイルチャンクは厳密に有限(例:単一ソースファイルを約 4 MiB に制限し、実行時に上書き可能)になります。変更間で中間データを永続化するために、モデルは生ポインタの代わりにインデックスを使用することを提案します。 成功すれば、この静的割り当てスタイルはコンパイラコードを簡素化し、メモリ不足エラーを減らし、保守性を向上させる可能性があります。これによりコンパイラ開発者、大規模ビルドシステム、および広範なソフトウェアツールエコシステム全体が恩恵を受けます。著者はこの分離を実験し、静的割り当てがデータベースシステムにもたらしたのと同じ明瞭さをもたらすかどうかを検証する予定です。
本文
2025年12月23日
TigerBeetle は「静的割り当て」を有名にしています。ここで言う「静的」とは、組み込み開発で使われる固定配列のことではなく、起動後にはメモリを確保しないという弱い形態です。TigerBeetle のプロセスが使用するメモリ量は ELF バイナリにハードコードされているわけではなく、実行時に渡すコマンドライン引数によって決まります。しかし、割り当ては起動時に一度だけ行われ、その後は解放もありません。長時間稼働するイベントループは、メモリ確保をせずに円環的に回転し続けます。
私は何年もの間、この手法がコンパイラにも応用できるかどうか疑問に思っていました。無理だと考えていたのですが、本日このアイデアから実践可能な示唆を得ることができました。
静的割り当て
-
基礎問題の物理学
分散型データベースは、少なくとも TigerBeetle の場合、驚くほど単純な「物理法則」を持っています。 -
入出力がメッセージである
各メッセージはサイズ(1 MiB)が有限です。システムの実際のデータはディスク上に保存され、任意に大きくなる可能性があります。しかし、単一メッセージによって適用される差分は有限です。入力と出力がともに有限なら、余分なメモリを必要とするケースは実際にはかなり限定的になります。 -
合成性
静的割り当ては常に監視し、資源を手動で管理しているように見えるかもしれませんが、実際には驚くほど合成的です。入力と出力が有限であれば、非確保処理は容易です。二つのシステムを組み合わせても問題なく機能します ― 例えば
は良い例です。routing.zig -
同時実行制限
唯一の問題は、同時に到着できるメッセージ数に物理的な上限がないことです。明らかに任意の数のメッセージを同時に処理することは不可能です。信頼性の低いネットワーク上で分散システムを動作させる場合、必要な処理資源が利用できないときにはメッセージを破棄する方が安全です。 -
単純さ
直感に反して、確保しないほうが確保よりも単純です。もし実現可能ならば!
コンパイラへの応用
残念ながら、コンパイラでこれを実現するのは不可能に思えます。「最大プログラムは 100 万関数以下」と言い切ることもできますが、それはメモリを無駄にし、ユーザー体験を損ないます。Hard Mode Rust のような固定サイズアリーナは静的割り当てとは異なります:アリーナではサイズが明示されるため OOM が起こり得ますが、静的割り当てなら OOM はなく、必要なメモリ量を知ることは起動完了まで分かりません。
コンパイラの「問題サイズ」は固定されていません ― 入力(ソースコード)も出力(実行ファイル)も任意に大きくなる可能性があります。しかし、TigerBeetle でも同様です:データベースサイズは固定ではなく、RAM ではなくディスクに保存されています。TigerBeetle はディスク上で静的割り当てを行っておらず、実行時に ENOSPACE で失敗する可能性がありますが、動的ブロックアロケータを使って不要になったセクタを再利用し、できるだけそれを回避しています。
したがって、次のように言えるでしょう:コンパイラは任意に大きな入力を消費し、任意に大きな出力を生成しますが、それらは「静的メモリ割り当て」の対象外です。起動時に「出力アリーナ」を確保して、コンパイラの作業結果である不変データを格納します。その後、処理したチャンクごとにこの出力を蓄積していきます。各チャンクサイズは必ず有限です。コードベース全体の総量を制限することは非現実的ですが、単一ファイルを 4 MiB(実行時上書き可能)までに抑えることなら妥当です。このようにコンパイラを「ストリーム処理」問題として扱うと、入力も出力も任意の大きさであっても、フィルタプログラム自体は O(1) メモリで実行されるべきです。
この設定では、出力データに対してポインタよりもインデックスを使う方が自然になります。これにより、変更間でディスクへ永続化するのが容易になり、また「変更チャンク」を空間的(新しいファイル)だけでなく時間的(古いファイルの新バージョン)にも考えることが奨励されます。
実際に得られるメリットはまだ不明ですが、試してみる価値はあると思います。O(N) のコンパイラ出力と O(1) 中間処理成果物を厳密に分離することで、コンパイラのアーキテクチャが明確になり、データベースで見られるような単純化されたコードが得られるかもしれません。