ジャストインタイムコンパイル

コンピューティングにおいて、ジャストインタイム( JIT )コンパイル(動的変換または実行時コンパイルとも呼ばれる) [ 1 ]は、プログラムの実行前ではなく実行時に (コンピュータ コードを) コンパイルすることです。[ 2 ]これソースコード変換構成される場合もありますが、より一般的にはバイトコードからマシン コードへの変換であり、その後マシン コードが直接実行されます。JIT コンパイラを実装しているシステムでは、通常、実行中のコードを継続的に分析し、コンパイルまたは再コンパイルによって得られる速度向上が、そのコードをコンパイルするオーバーヘッドを上回るコード部分を特定します。

JIT コンパイルは、機械語への変換に対する 2 つの従来の手法、つまり事前コンパイル(AOT) と解釈を組み合わせたもので、両方のメリットとデメリットを兼ね備えています。[ 2 ]大まかに言うと、JIT コンパイルは、コンパイルされたコードの速度と解釈の柔軟性、インタープリタのオーバーヘッド、コンパイルとリンク(解釈だけでなく) の追加のオーバーヘッドを組み合わせたものです。JIT コンパイルは動的コンパイルの一種で、動的再コンパイルマイクロアーキテクチャ固有の高速化などの適応型最適化を可能にします。[注 1 ] [ 3 ]解釈と JIT コンパイルは、ランタイム システムが遅延バインディングデータ型を 処理し、セキュリティ保証を実施できるため、動的プログラミング言語に特に適しています。

歴史

最も古い JIT コンパイラは、一般的に1960 年のJohn McCarthyによるLISPの研究に帰せられます。 [ 4 ]彼の独創的な論文「記号式の再帰関数と機械によるその計算、パート I」では、実行時に翻訳される関数について言及されており、これによってコンパイラの出力をパンチ カードに保存する必要がなくなりました[ 5 ](ただし、これはより正確には「コンパイル アンド ゴー システム」として知られています)。もう 1 つの初期の例はKen Thompsonによるもので、彼は 1968 年に、この例ではテキスト エディタQEDでのパターン マッチングに、正規表現の最初の応用例の 1 つを示しました。[ 6 ]速度を重視して、Thompson は互換タイム シェアリング システム上のIBM 7094コードにJITすることによって正規表現マッチングを実装しました。[ 4 ] [ 7 ] [ 8 ]

Smalltalk(1980年頃)は、JITコンパイルの新たな側面を開拓しました。例えば、機械語への変換はオンデマンドで行われ、結果は後で使用するためにキャッシュされます。メモリが不足すると、システムはこのコードの一部を削除し、再び必要になったときに再生成します。[ 2 ] [ 9 ] SunのSelf言語はこれらの技術を大幅に改良し、一時は世界最速のSmalltalkシステムとなり、最適化されたC言語の最大半分の速度[ 10 ]を達成しながらも、完全なオブジェクト指向プログラミング言語でした。

SelfはSunによって放棄されましたが、その研究はJava言語へと移されました。「ジャストインタイムコンパイル」という用語は、製造業の用語「ジャストインタイム」から借用され、Javaによって普及しました。James Goslingは1993年にこの用語を使用しました。[ 11 ]現在、JITはJava仮想マシンのほとんどの実装で採用されており、HotSpotはこの研究基盤を基盤として、広範囲に活用しています。

HPプロジェクトDynamoは、バイトコード形式とマシンコード形式が同じ実験的なJITコンパイラであり、PA-8000マシンコードを最適化しました。[ 12 ]直感に反して、これは速度向上をもたらし、場合によっては30%も向上しました。これは、マシンコードレベルでの最適化、例えばキャッシュ利用効率を向上させるためのコードのインライン化や、動的ライブラリ呼び出しの最適化、従来のコンパイラでは不可能な多くの実行時最適化が可能になったためです。[ 13 ] [ 14 ]

2020年11月、PHP 8.0でJITコンパイラが導入されました。[ 15 ] 2024年10月、CPythonで実験的なJITコンパイラが導入されました。[ 16 ]

デザイン

バイトコードコンパイルシステムでは、ソースコードはバイトコードと呼ばれる中間表現に変換されます。バイトコードは特定のコンピュータのマシンコードではなく、コンピュータアーキテクチャ間で移植可能です。バイトコードは仮想マシンによって解釈または実行される場合があります。JITコンパイラは、バイトコードを複数のセクション(まれに全体)で読み取り、動的にマシンコードにコンパイルすることで、プログラムの実行速度を向上させます。これはファイルごと、関数ごと、あるいは任意のコードフラグメントごとに実行できます。コードは実行される直前にコンパイルされ(そのため「ジャストインタイム」と呼ばれます)、キャッシュされて後で再コンパイルすることなく再利用できます。

対照的に、従来のインタープリタ型仮想マシンはバイトコードを単純に解釈するだけなので、一般的にパフォーマンスははるかに低くなります。一部のインタープリタは、バイトコードへのコンパイルをせずにソースコードを解釈しますが、そのパフォーマンスはさらに低くなります。静的にコンパイルされたコード、つまりネイティブコードは、デプロイ前にコンパイルされます。動的コンパイル環境とは、実行中にコンパイラを使用できる環境です。JIT技術を使用する一般的な目標は、バイトコード解釈の利点を維持しながら、静的コンパイルのパフォーマンスに匹敵するか、それを上回ることです。元のソースコードの解析や基本的な最適化といった「重労働」の多くは、多くの場合、デプロイ前のコンパイル時に処理されます。バイトコードからマシンコードへのコンパイルは、ソースコードからコンパイルするよりもはるかに高速です。デプロイされたバイトコードは、ネイティブコードとは異なり、移植可能です。インタープリタ型バイトコードと同様に、ランタイムがコンパイルを制御するため、安全なサンドボックス内で実行できます。バイトコードからマシンコードへのコンパイラは、移植性の高いバイトコードコンパイラが既に多くの作業を行っているため、作成が容易です。

JITコードは一般的にインタープリタよりもはるかに優れたパフォーマンスを提供します。さらに、多くの最適化が実行時にのみ実行可能であるため、場合によっては静的コンパイルよりも優れたパフォーマンスを発揮します。[ 17 ] [ 18 ]

  1. コンパイルは、対象となるCPUとアプリケーションが実行されるオペレーティングシステムモデルに合わせて最適化できます。例えば、JITコンパイラはCPUがSSE2ベクターCPU命令をサポートしていることを検出すると、それを選択します。静的コンパイラでこのレベルの最適化を実現するには、対象とするプラットフォーム/アーキテクチャごとにバイナリをコンパイルするか、コードの一部の複数のバージョンを単一のバイナリに含める必要があります。
  2. システムは、プログラムが実際に環境内でどのように動作しているかに関する統計情報を収集し、最適なパフォーマンスを得るために再配置と再コンパイルを行うことができます。ただし、一部の静的コンパイラはプロファイル情報を入力として受け取ることもできます。
  3. システムは、動的リンクの利点を失うことなく、また静的コンパイラやリンカーに固有のオーバーヘッドなしに、グローバルなコード最適化(ライブラリ関数のインライン化など)を行うことができます。具体的には、グローバルなインライン置換を行う際に、静的コンパイルプロセスでは実行時チェックが必要となる場合があります。オブジェクトの実際のクラスがインラインメソッドをオーバーライドする場合、仮想呼び出しが行われることを確認する必要があるほか、配列アクセスの境界条件チェックをループ内で処理する必要がある場合があります。ジャストインタイムコンパイルでは、多くの場合、この処理をループ外に移動できるため、速度が大幅に向上します。
  4. これは静的にコンパイルされたガベージコレクション言語でも可能ですが、バイトコード システムでは、実行されたコードをより簡単に再配置して、キャッシュをより有効に活用できます。

JITは実行時にネイティブバイナリイメージをレンダリングして実行する必要があるため、真のマシンコードJITは実行時にデータを実行できるプラットフォームを必要とし、ハーバードアーキテクチャベースのマシンではそのようなJITの使用は不可能です。これは特定のオペレーティングシステムや仮想マシンにも当てはまります。しかし、特殊なタイプの「JIT」は、物理マシンのCPUアーキテクチャではなく、生のマシンコードに制限がある最適化されたVMバイトコードをターゲットとする可能性があります特に、そのバイトコードのVMが最終的にJITをネイティブコードに活用する場合に当てはまります。[ 19 ]

パフォーマンス

JITは、入力コードの読み込みとコンパイルにかかる時間により、アプリケーションの初期実行時にわずかな遅延から顕著な遅延を引き起こします。この遅延は「起動時間遅延」または「ウォームアップ時間」と呼ばれることもあります。一般的に、JITによる最適化が進むほど、生成されるコードはより高品質になりますが、初期遅延も増加します。そのため、JITコンパイラは、コンパイル時間と生成するコードの品質との間でトレードオフを行う必要があります。起動時間には、JITコンパイルに加えて、IOバウンドな操作の増加が含まれる場合があります。例えば、Java仮想マシン(JVM)のrt.jarクラスデータファイルは40MBであり、JVMはこのコンテキスト的に巨大なファイル内で大量のデータをシークする必要があります。[ 20 ]

SunのHotSpot Java仮想マシンで採用されている最適化手法の一つは、解釈とJITコンパイルを組み合わせることである。アプリケーションコードはまず解釈されるが、JVMはどのバイトコードシーケンスが頻繁に実行されるかを監視し、それらをハードウェア上で直接実行できるようにマシンコードに変換する。数回しか実行されないバイトコードの場合、これによりコンパイル時間が節約され、初期のレイテンシが短縮される。頻繁に実行されるバイトコードの場合、最初の低速な解釈フェーズの後、JITコンパイルによって高速実行される。さらに、プログラムはほとんどの時間をコードの一部の実行に費やしているため、コンパイル時間の短縮は顕著である。最後に、初期のコード解釈段階では、コンパイル前に実行統計を収集することができ、より効果的な最適化を行うのに役立つ。[ 21 ]

適切なトレードオフは状況によって異なります。例えば、SunのJava仮想マシンには、クライアントとサーバーという2つの主要モードがあります。クライアントモードでは、最小限のコンパイルと最適化が実行され、起動時間が短縮されます。サーバーモードでは、広範なコンパイルと最適化が実行され、起動時間を犠牲にしてアプリケーションの実行後のパフォーマンスが最大化されます。他のJavaジャストインタイムコンパイラは、メソッドの実行回数とバイトコードサイズを組み合わせた実行時測定をヒューリスティックとして利用し、コンパイルのタイミングを決定します。[ 22 ]また、実行回数とループの検出を組み合わせて使用​​するものもあります。[ 23 ]一般的に、短時間実行のアプリケーションでは、長時間実行のアプリケーションよりもどのメソッドを最適化すべきかを正確に予測することがはるかに困難です。[ 24 ]

Microsoftのネイティブイメージジェネレータ(Ngen)は、初期遅延を短縮するもう一つのアプローチです。[ 25 ] Ngenは、共通中間言語(CIL)イメージ内のバイトコードをマシンネイティブコードにプリコンパイル(または「プリJIT」)します。そのため、実行時のコンパイルは不要です。Visual Studio 2005に同梱されている.NET Framework 2.0は、インストール直後にすべてのMicrosoftダイナミックリンクライブラリ(DLL)ファイルに対してNgenを実行します。プリJITは起動時間を短縮する手段を提供します。しかし、生成されるコードの品質はJITコンパイルされたコードよりも低くなる可能性があります。これは、プロファイルに基づく最適化(profile-guided optimization)を行わずに静的にコンパイルされたコードが、極端な場合にはJITコンパイルされたコードほど良くならないのと同じ理由です。つまり、例えばインラインキャッシュを駆動するためのプロファイリングデータが不足しているのです。[ 26 ]

AOT (Ahead-of-Time) コンパイラと JIT コンパイラ ( Excelsior JET ) またはインタープリター ( GNU Compiler for Java ) を組み合わせた Java 実装も存在します。

JIT コンパイルは、短い初期ウォームアップ期間の後にパフォーマンスが向上した定常状態に入るという目標を、確実に達成できない可能性があります。[ 27 ] [ 28 ] 8 つの異なる仮想マシンで、Barrett ら (2017) は、仮想マシンの実装者が最適化ターゲットとして一般的に使用する 6 つの広く使用されているマイクロベンチマークを測定し、単一のプロセス実行内で繰り返し実行しました。[ 29 ] Linuxでは、プロセス実行の 8.7% ~ 9.6% が安定したパフォーマンス状態に到達できず、16.7% ~ 17.9% がウォームアップ期間後にパフォーマンスが低下した定常状態に入り、特定のベンチマークを実行する特定の仮想マシンの 56.5% の組み合わせが、複数の実行にわたって安定状態でパフォーマンスが低下しないことを一貫して確認できませんでした (つまり、少なくとも 1 つの実行が定常状態に到達できなかったか、定常状態でパフォーマンスが低下しました)。改善された定常状態に到達した場合でも、数百回の反復が必要になることがありました。[ 30 ] Trainiら(2022)は、 HotSpot仮想マシンに焦点を当てましたが、より幅広いベンチマークを使用し、[ 31 ]プロセス実行の10.9%が安定したパフォーマンス状態に到達できず、ベンチマークの43.5%が複数の実行にわたって一貫して安定状態に到達しなかったことを発見しました。[ 32 ]

安全

JIT コンパイルは基本的に実行可能データを使用するため、セキュリティ上の課題や悪用される可能性があります。

JITコンパイルの実装は、ソースコードまたはバイトコードをマシンコードにコンパイルし、それを実行することから成ります。これは通常、メモリ内で直接行われます。つまり、JITコンパイラはマシンコードをメモリに直接出力し、すぐに実行します。これは、通常の事前コンパイルのように、マシンコードをディスクに出力してから別のプログラムとしてコードを呼び出すのではなく、メモリに直接出力します。現代のアーキテクチャでは、実行可能空間の保護のために問題が発生します。任意のメモリを実行すると潜在的なセキュリティホールになる可能性があるためです。したがって、メモリは実行可能としてマークする必要があります。セキュリティ上の理由から、これはコードがメモリに書き込まれた後に行われ、書き込み可能/実行可能なメモリはセキュリティホールであるため、読み取り専用としてマークする必要があります(W^Xを参照)。[ 33 ]たとえば、FirefoxのJavaScript用JITコンパイラは、Firefox 46のリリースバージョンでこの保護を導入しました。[ 34 ]

JIT スプレーは、ヒープ スプレーに JIT コンパイルを使用するコンピューター セキュリティ エクスプロイトの一種です。結果のメモリは実行可能になり、実行をヒープに移動できる場合はエクスプロイトが可能になります。

用途

JIT コンパイルは一部のプログラムに適用できますが、特に正規表現などの動的な機能に使用できます。たとえば、テキスト エディターでは、実行時に提供された正規表現をマシン コードにコンパイルして、マッチングを高速化できます。パターンは実行時にのみ提供されるため、これを事前に行うことはできません。多くの最新のランタイム環境は、高速コード実行のために JIT コンパイルに依存しており、これにはJavaのほとんどの実装やMicrosoft.NETが含まれます。同様に、多くの正規表現ライブラリでは、バイトコードまたはマシン コードへの正規表現の JIT コンパイル機能を備えています。JIT コンパイルは、一部のエミュレーターでも、マシン コードを 1 つの CPU アーキテクチャから別の CPU アーキテクチャに変換するために使用されています。

JITコンパイルの一般的な実装は、まずバイトコード(仮想マシンコード)へのAOTコンパイル(バイトコードコンパイル)を行い、次にバイトコードを解釈せずにマシンコード(動的コンパイル)へのJITコンパイルを行うというものです。これにより、解釈コンパイルに比べて実行時のパフォーマンスが向上しますが、コンパイルによる遅延が発生します。JITコンパイラは、インタープリタと同様に継続的に翻訳を行いますが、コンパイル済みのコードをキャッシュすることで、特定の実行中に同じコードを実行する際の遅延を最小限に抑えます。プログラムの一部のみがコンパイルされるため、プログラム全体を実行前にコンパイルする場合に比べて、遅延が大幅に軽減されます。

参照

注記

  1. ^ AOTコンパイラも特定のマイクロアーキテクチャをターゲットにできますが、この点におけるAOTとJITの違いは移植性にあります。JITは実行時に現在実行中のCPUに合わせてコードをレンダリングできますが、AOTは汎用的なマイクロアーキテクチャのサブセット向けに最適化するのではなく、ターゲットCPUを事前に認識しておく必要があります。そのため、このようなコードは他のCPUタイプではパフォーマンスが劣るだけでなく、完全に不安定になる可能性があります。

参考文献

  1. ^言語、コンパイラ、ランタイムシステム、ミシガン大学、コンピュータサイエンスおよびエンジニアリング、2018年3月26日時点のオリジナルからアーカイブ、 2018年3月15日閲覧。
  2. ^ a b cエイコック 2003 .
  3. ^ 「JITはCPUを有効活用するのか?」 David NotarioのWebLog 。 2018年12月3日閲覧
  4. ^ a b Aycock 2003、2 . JIT コンパイル手法、2.1 Genesis、p. 98。
  5. ^ McCarthy, J. (1960年4月). 「記号式の再帰関数と機械による計算、第1部」Communications of the ACM . 3 (4): 184– 195. CiteSeerX 10.1.1.111.8833 . doi : 10.1145/367177.367199 . S2CID 1489409 .  
  6. ^トンプソン 1968 .
  7. ^ Aycock 2003、2 . JIT コンパイルテクニック、2.2 LC²、p. 98–99。
  8. ^ Mitchell, JG (1970). 「柔軟かつ効率的な対話型プログラミングシステムの設計と構築」{{cite journal}}:ジャーナルを引用するには|journal=ヘルプ)が必要です
  9. ^ Deutsch, LP; Schiffman, AM (1984). 「Smalltalk-80システムの効率的な実装」(PDF) .第11回ACM SIGACT-SIGPLANシンポジウム「プログラミング言語の原理 - POPL '84」の議事録. pp.  297– 302. doi : 10.1145/800017.800542 . ISBN 0-89791-125-3. S2CID  3045432 . 2004年6月18日時点のオリジナル(PDF)からアーカイブ。
  10. ^ "97-pep.ps" . research.sun.com . 2006年11月24日時点のオリジナルよりアーカイブ2022年1月15日閲覧。
  11. ^ Aycock 2003、2.14 Java、p.107、脚注13。
  12. ^「Dynamo: 透過的な動的最適化システム」Vasanth Bala、Evelyn Duesterwald、Sanjeev Banerjia共著。PLDI '00 Proceedings of the ACM SIGPLAN 2000 conference on Programming language design and implementation. 1~12ページ。DOI 10.1145/349299.349303。2012年3月28日閲覧。
  13. ^ John Jannotti. 「HPのDynamo」 . Ars Technica . 2013年7月5日閲覧。
  14. ^ “The HP Dynamo Project” . 2002年10月19日時点のオリジナルよりアーカイブ2016年4月12日閲覧。
  15. ^ Tung, Liam (2020年11月27日). 「プログラミング言語PHP 8がリリース:この新しいJITコンパイラはパフォーマンス向上を示唆」 ZDNet . 2020年11月28日閲覧
  16. ^ 「What's New In Python 3.13」 . Pythonドキュメント. 2024年11月27日閲覧。
  17. ^ Croce, Louis. 「Just in Time Compilation」(PDF) .コロンビア大学. 2018年5月3日時点のオリジナル(PDF)からアーカイブ
  18. ^ 「JITコンパイルとAOTコンパイルの違いは何ですか?」 Stack Overflow、2010年1月21日。
  19. ^ 「JITベースの言語をWebassemblyにコンパイルする」 . Stack Overflow . 2018年12月4日閲覧。
  20. ^ Haase, Chet (2007年5月). 「コンシューマー向けJRE:よりスリムで強力なJavaテクノロジー」 . Sun Microsystems . 2007年7月27日閲覧。
  21. ^ 「Java HotSpotパフォーマンスエンジンアーキテクチャ」 . Oracle.com . 2013年7月5日閲覧。
  22. ^ Schilling, Jonathan L. (2003年2月). 「Java JITコンパイラでは、最も単純なヒューリスティックが最善かもしれない」(PDF) . SIGPLAN Notices . 38 (2): 36– 46. doi : 10.1145/772970.772975 . S2CID 15117148. 2015年9月24日時点のオリジナル(PDF)からのアーカイブ 
  23. ^菅沼敏夫、安江敏明、川人基博、小松秀明、中谷敏夫、「Java ジャストインタイム コンパイラのための動的最適化フレームワーク」、オブジェクト指向プログラミング、システム、言語、およびアプリケーションに関する第 16 回 ACM SIGPLAN カンファレンス(OOPSLA '01) の議事録、180 ~ 195 ページ、2001 年 10 月 14 ~ 18 日。
  24. ^ Matthew Arnold、Michael Hind、Barbara G. Ryder、「選択的最適化の経験的研究」、並列コンピューティングのための言語とコンパイラに関する第13回国際ワークショップの議事録-改訂論文、pp. 49–67、2000年8月10日~12日。
  25. ^ "ネイティブ イメージ ジェネレーター (Ngen.exe)" . Msdn2.microsoft.com。 2006 年 12 月 5 日2013 年 7 月 5 日に取得
  26. ^ Sweeney, Arnold (2005年2月). 「仮想マシンにおける適応型最適化の概説」(PDF) . Proceedings of the IEEE 92 ( 2): 449– 466. 2016年6月29日時点のオリジナル(PDF)からのアーカイブ。
  27. ^バレットら 2017、3ページ。
  28. ^ Traini et al. 2022、p.1。
  29. ^バレットら 2017、5-6頁。
  30. ^バレットら 2017、p.12-13。
  31. ^トレイニら。 2022、p. 17-23。
  32. ^トレイニら。 2022、p. 26-29。
  33. ^「JIT のやり方 – 入門」、Eli Bendersky、2013年11月5日午前5時59分
  34. ^ De Mooij、1 月、「Firefox で W^X JIT コードが有効化」ヤン・デ・モーイ2016 年5 月 11 日に取得

参考文献