マルチスレッド プログラムで JIT アセンブラの異なる呼び出し間でページを共有する方法はありますか?
私は小さなJITエンジンを構築しています。JITting の背後にある基本原則は十分に単純なようです:mmap
いくつかのスペース、マシン コードをその中に入れ、mprotect
それを実行可能にし、コード ブロックの先頭を関数ポインターにキャストします。
ただし、mmap
/のmprotect
組み合わせ (および Windows の同等のもの) は、ページ全体でのみ機能します (これは正しいですか? そうでない場合は、質問全体が破棄されます)。これは、JIT アセンブラが実行されるたびに、少なくともページ全体のスペースを割り当てる必要があることを意味します。これは、いくつかの大きな関数やプログラム全体を一度に処理する JIT にとっては大したことではありませんが、私が主に関心を持っていたのは、非常に短いコード スニペットの処理でした。割り当てられたスペースのほとんどは、生成されたコードでは使用されません。
シングルスレッド アプリケーションでは、これは問題になりません。JITter が調整する番になるとmprotect
、その時点でコードが実行されていないことを認識して、コードを心ゆくまでバッファリングできます。したがって、このスペースは、実際にいっぱいになるまで、後続の機能に使用し続けることができます。残念ながら、シングルスレッドを想定したり要求したりすることは、今では少し前世紀のようです。
マルチスレッド アプリケーションでは、動的に作成されたコードが、他のコードの生成と同時に既に実行されている場合があります。JITter が保護をいじろうとすると、何かが壊れます。そのような状況に対処するためのオプションは何ですか?
これまでに検討したこと:
問題を無視して、プロシージャごとに新しいバッファを割り当てます。実際には、メモリ使用量の顕著な増加を引き起こすには何千もの割り当てが必要であり、これはデスクトップ上でうまく機能する可能性があります。しかし、哲学的には、「たくさんのハードウェアを持っていることを台無しにしてください」や「誰もこのシステムにストレスを与えることはありません」と言うのは、設計の観点からはちょっと嫌です。
書き込み + 実行保護を使用してバッファーを割り当てます。これは明白な答えのように思えますが、それを正当化する経験が私にないという悪い気持ちを私に与えます (これは、
mprotect
そもそも JIT や を呼び出すプログラムを持つことよりも悪いことですか?)。安全ですか?すべての JITted 関数がそのコード バッファーでロックを取得しようとするようにします。これは (直観的に) 潜在的にパフォーマンスを低下させ、コード サイズを膨張させ、単純に実行された場合、JITter が何も実行できなくなる可能性があるように思えます。
JITter にコード バッファへの優先アクセスを自由に与え
mprotect
、これにより既存の JITted プロシージャが segfault になる場合は、シグナル ハンドラでそれらをキャッチし、JITter が完了するまで待機します。これが実際に可能かどうかはわかりませんが...ええ、それはアイデアです。
いくつかの既存の JIT コンパイラのソースを調べてみましたが、ほとんどの場合、それらは大規模なシステムであり、理解するのに非常に長い時間がかかり、理解できませんでした。明確な答えのある小さな例ではありません。 (ソースを読みたくないわけではありません。JavaScriptCore または V8 から必要な情報を取得できませんでした。より単純な例は大歓迎です)。とにかく、この種のことは、実際にはいくつかのプロジェクトにとって未解決の問題のようです。