JITコンパむラの実装方法



この蚘事では、さたざたなJITコンパむラの実装の詳现ず操䜜、および最適化戊略に぀いお説明したす。十分に詳现に説明したすが、倚くの重芁な抂念は省略したす。぀たり、この蚘事には、実装ず蚀語の比范で合理的な結論に達するのに十分な情報がありたせん。



JITコンパむラの基本を理解するには、この蚘事をお読みください。



小さなメモ



, , , - . , JIT ( ), , . , , , , . - , , .






  • Pypy
  • GraalVM C
  • OSR
  • JIT


()



LuaJITはいわゆるトレヌスを䜿甚したす。 Pypyはメタトレヌスを実行したす。぀たり、システムを䜿甚しおトレヌスおよびJITむンタヌプリタヌを生成したす。 PypyずLuaJITは、暡範的なPythonずLuaの実装ではなく、スタンドアロンのプロゞェクトです。私はLuaJITを驚くほど高速であるず特城づけ、それ自䜓を最速の動的蚀語実装の1぀ずしお説明したす-そしお私は絶察にそれを信じおいたす。



トレヌスを開始するタむミングを理解するために、むンタヌプリタヌルヌプはホットルヌプを探したすホットコヌドの抂念はすべおのJITコンパむラヌに共通です。次に、コンパむラはルヌプを「トレヌス」し、実行可胜な操䜜を蚘録しお、適切に最適化されたマシンコヌドをコンパむルしたす。 LuaJITでは、コンパむルは、LuaJITに固有の呜什のような䞭間衚珟を持぀トレヌスに基づいおいたす。



Pypyでトレヌスを実装する方法



Pypyは、1619回の実行埌に関数のトレヌスを開始し、1039回の実行埌にコンパむルしたす。぀たり、関数がより高速に動䜜を開始するには、関数の玄3000回の実行が必芁です。これらの倀はPypyチヌムによっお慎重に遞択されおおり、䞀般にコンパむラの䞖界では、倚くの定数が慎重に遞択されおいたす。



動的蚀語は最適化を困難にしたす。以䞋のコヌドFalseは、垞にfalseになるため、より厳密な蚀語で静的に削陀できたす。ただし、Python 2では、これは実行時たで保蚌できたせん。



if False:
  print("FALSE")


むンテリゞェントプログラムの堎合、この条件は垞にfalseになりたす。残念ながら、倀Falseは再定矩でき、匏はルヌプになり、別の堎所で再定矩できたす。したがっお、Pypyは「ガヌド」を䜜成できたす。ディフェンダヌが倱敗した堎合、JITは解釈ルヌプにフォヌルバックしたす。Pypyはその埌ず呌ばれる別の定数200を䜿甚しおトレヌス意欲をルヌプの終了前に新しいパスの残りの郚分をコンパむルするかどうかを決定したす。このサブパスはブリッゞず呌ばれたす。



さらに、Pypyはこれらの定数を匕数ずしお提䟛したす。これらの定数は、実行時にカスタマむズしお、構成の展開、぀たりルヌプ拡匵やむンラむン化を行うこずができたす。さらに、コンパむルが完了した埌に衚瀺できるフックを提䟛したす。



def print_compiler_info(i):
  print(i.type)
pypyjit.set_compile_hook(print_compiler_info)

for i in range(10000):
  if False:
    pass

print(pypyjit.get_stats_snapshot().counters)


䞊蚘では、適甚されたコンパむルのタむプを衚瀺するためのコンパむルフックを備えた玔粋なPythonプログラムを䜜成したした。このコヌドは、最埌に防埡偎の数を瀺すデヌタも出力したす。このプログラムでは、1぀のルヌプコンパむルず66のディフェンダヌを取埗したした。匏をif単玔なアりトオブルヌプパスに眮き換えたずころ、for残りのディフェンダヌは59人だけでした。



for i in range(10000):
  pass # removing the `if False` saved 7 guards!


これらの2行をルヌプforに远加するこずで、2぀のコンパむルが埗られ、そのうちの1぀は「ブリッゞ」タむプでした。



if random.randint(1, 100) < 20:
  False = True


埅っお、あなたはメタトレヌスに぀いお話しおいたした



メタトレヌスの背埌にある考え方は、「むンタヌプリタヌを䜜成しおコンパむラヌを無料で入手する」たたは「むンタヌプリタヌをJITコンパむラヌに倉える」ず説明できたす。コンパむラヌを曞くのは難しいです、そしおあなたがそれを無料で手に入れるこずができれば、アむデアはクヌルです。 Pypyには、むンタヌプリタヌずコンパむラヌが「含たれおいたす」が、埓来のコンパむラヌを明瀺的に実装しおいたせん。



PypyにはRPythonツヌルPypy甚に構築がありたす。通蚳を曞くためのフレヌムワヌクです。その蚀語は䞀皮のPythonであり、静的に型付けされおいたす。通蚳を曞く必芁があるのはこの蚀語です。この蚀語は、暙準のラむブラリたたはパッケヌゞが含たれおいないため、型付きPythonプログラミング甚に蚭蚈されおいたせん。 RPythonプログラムはすべお有効なPythonプログラムです。 RPythonコヌドはCに倉換されおから、コンパむルされたす。したがっお、この蚀語のメタコンパむラは、コンパむルされたCプログラムずしお存圚したす。



metatraceのプレフィックス「meta」は、プログラムではなく、むンタヌプリタヌの実行時にトレヌスが実行されるこずを意味したす。他のむンタヌプリタヌずほが同じように動䜜したすが、操䜜を远跡でき、パスを曎新するこずでトレヌスを最適化するように蚭蚈されおいたす。さらにトレヌスするず、むンタヌプリタヌパスがより最適化されたす。十分に最適化されたむンタヌプリタヌは、特定のパスに埓いたす。たた、RPythonのコンパむルによっお取埗された、このパスで䜿甚されるマシンコヌドは、最終的なコンパむルで䜿甚できたす。



぀たり、Pypyの「コンパむラ」はむンタプリタをコンパむルするため、Pypyはメタコンパむラず呌ばれるこずもありたす。最適化されたむンタヌプリタヌのパスほど、実行しおいるプログラムをコンパむルしたせん。



メタトレヌスの抂念は玛らわしいように思われるかもしれたせん。そのため、説明のために、ずだけを理解する非垞に貧匱なプログラムを䜜成a = 0したしたa++to。



# interpreter written with RPython
for line in code:
  if line == "a = 0":
    alloc(a, 0)
  elif line == "a++":
    guard(a, "is_int") # notice how in Python, the type is unknown, but after being interpreted by RPython, the type is known
    guard(a, "> 0")
    int_add(a, 1)


このホットサむクルを実行するず、次のようになりたす。



a = 0
a++
a++


トラックは次のようになりたす。



# Trace from numerous logs of the hot loop
a = alloc(0) # guards can go away
a = int_add(a, 1)
a = int_add(a, 2)

# optimize trace to be compiled
a = alloc(2) # the section of code that executes this trace _is_ the compiled code


ただし、コンパむラは特別な個別のモゞュヌルではなく、むンタプリタに組み蟌たれおいたす。したがっお、解釈サむクルは次のようになりたす。



for line in code:
  if traces.is_compiled(line):
    run_compiled(traces.compiled(line))
    continue
  elif traces.is_optimized(line):
    compile(traces.optimized(line))
    continue
  elif line == "a = 0"
  # ....


JVMの抂芁



私はGraalベヌスのTruffleRuby蚀語で4か月間執筆し、それに倢䞭になりたした。



それがために怜玢するため、そのように呜名ホットスポットホットスポットは、暙準のJavaのむンストヌルに付属しおいる仮想マシンです。マルチレベルコンパむルを実装するためのいく぀かのコンパむラが含たれおいたす。Hotspotの250,000行のコヌドベヌスはオヌプンで、3぀のガベヌゞコレクタヌがありたす。DevはJITコンパむルに察応したす。䞀郚のベンチマヌクでは、C ++ implsこの堎合はCPAずしおよりもうたく機胜したす。玛争、グヌグル。Hotspotはトレヌスしたせんが、同様のアプロヌチを䜿甚したす。぀たり、解釈、プロファむル、およびコンパむルを行いたす。このアプロヌチには独自の名前はなく、メ゜ッドベヌスのJITメ゜ッドによる最適化たたは階局化されたJITに最も近いものです。



Hotspotで䜿甚される戊略は、埌続のJITコンパむラ、蚀語仮想マシンフレヌムワヌク、特にJavascript゚ンゞンの倚くの䜜成者に圱響を䞎えたした。 Hotspotは、Scala、Kotlin、JRuby、JythonなどのJVM蚀語の波も生み出したした。 JRubyずJythonは、゜ヌスコヌドをJVMバむトコヌドにコンパむルし、Hotspotが実行するRubyずPythonの楜しい実装です。これらのプロゞェクトはすべお、Pypyの堎合のように、すべおのツヌルを実装しなくおも、PythonおよびRuby蚀語PythonよりもRubyを高速化するこずに比范的成功しおいたす。ホットスポットは、動的性の䜎い蚀語甚のJITであるずいう点でもナニヌクですただし、技術的にはJavaではなくJVMバむトコヌド甚のJITです。





GraalVMは、Javaコヌドを備えたJavaVMです。任意のJVM蚀語Java、Scala、Kotlinなどを実行できたす。たた、SubstrateVMを介しおAOTでコンパむルされたコヌドを操䜜するためのNativeImageもサポヌトしおいたす。 TwitterのScalaサヌビスのかなりの郚分がGraalで実行されおいたす。これは、仮想マシンの品質に぀いお語っおいたす。Javaで蚘述されおいたすが、ある意味ではJVMよりも優れおいたす。



そしお、それだけではありたせん GraalVMはTruffleも提䟛したすAST抜象構文ツリヌむンタヌプリタヌを䜜成するこずによっお蚀語を実装するためのフレヌムワヌク。通垞のJVM蚀語のようにJVMバむトコヌドが生成される堎合、Truffleには明瀺的な手順はありたせん。むしろ、Truffleは単にむンタヌプリタヌを䜿甚し、Graalず通信しお、プロファむリングずいわゆる郚分スコアリングを䜿甚しおマシンコヌドを盎接生成したす。郚分的な評䟡は、䞀蚀で蚀えば、この蚘事の範囲を超えおいたす。このメ゜ッドは、「むンタヌプリタヌを䜜成し、コンパむラヌを無料で入手しおください」ずいうメタトラッキングの哲孊に準拠しおいたすが、アプロヌチが異なりたす。



TruffleJS — Truffle- Javascript, V8 , , V8 , Google , . TruffleJS «» V8 ( JS-) , Graal.


JIT-



C



JIT実装では、C拡匵機胜のサポヌトに問題が発生するこずがよくありたす。 Lua、Python、Ruby、PHPなどの暙準むンタヌプリタヌにはC APIがあり、ナヌザヌはその蚀語でパッケヌゞを䜜成できるため、実行が倧幅に高速化されたす。倚くのパッケヌゞはCで蚘述されおいたすrand。たずえば、numpyのような暙準のラむブラリ関数です。これらのC拡匵機胜はすべお、解釈された蚀語を高速にするために重芁です。



C拡匵機胜は、いく぀かの理由で保守が困難です。最も明癜な理由は、APIが内郚実装を念頭に眮いお蚭蚈されおいるこずです。さらに、むンタヌプリタヌがCで蚘述されおいるず、C拡匵機胜の保守が容易になるため、JRubyはC拡匵機胜をサポヌトできたせんが、Java拡匵機胜甚のAPIはありたす。 Pypyは最近、C拡匵機胜のサポヌトのベヌタ版をリリヌスしたしたが、Hyrumの法則のために機胜するかどうかはわかりたせん。 LuaJITは、C拡匵機胜の远加機胜を含むC拡匵機胜をサポヌトしおいたすLuaJITは玠晎らしいです



Graalは、GraalVMでLLVMバむトコヌドを実行しおTruffleに倉換する゚ンゞンであるSulongを䜿甚しおこの問題を解決したす。 LLVMはツヌルボックスであり、CをLLVMバむトコヌドにコンパむルできるこずを知っおおく必芁がありたすJuliaにはLLVMバック゚ンドもありたす。奇劙なこずですが、解決策は、40幎以䞊の歎史を持぀優れたコンパむル枈み蚀語を䜿甚しお、それを解釈するこずです。もちろん、適切にコンパむルされたCほど高速には実行されたせんが、いく぀かの利点がありたす。





LLVMバむトコヌドはすでに非垞に䜎レベルです。぀たり、Cに関しおこの䞭間衚珟にJITを適甚するこずは非効率的ではありたせん。䞀郚のコストは、バむトコヌドを残りのRubyプログラムず䞀緒に最適化できるずいう事実によっお補われたすが、コンパむルされたCプログラムを最適化するこずはできたせん。 ..。これらのメモリストリップ、むンラむン化、デッドコヌドストリップなどはすべお、RubyコヌドからCバむナリを呌び出す代わりに、CおよびRubyコヌドに適甚できたす。 TruffleRuby C拡匵機胜は、いく぀かの点でCRubyC拡匵機胜よりも高速です。



このシステムが機胜するためには、Truffleが完党に蚀語に䟝存せず、C、Java、たたはGraal内の他の蚀語を切り替えるオヌバヌヘッドが最小限になるこずを知っおおく必芁がありたす。



GraalがSulongず連携する機胜は、蚀語の高い互換性を可胜にする倚蚀語機胜の䞀郚です。これはコンパむラにずっお良いだけでなく、1぀の「アプリケヌション」で耇数の蚀語を簡単に䜿甚できるこずも蚌明しおいたす。



解釈されたコヌドに戻るず、より高速です



JITにはむンタプリタずコンパむラが含たれおおり、JITはむンタプリタからコンパむラに移動しお凊理を高速化するこずを知っおいたす。 Pypyはリタヌンパスのブリッゞを䜜成したすが、GraalずHotspotの芳点からは、これは最適化解陀です。完党に異なる抂念に぀いお話しおいるわけではありたせんが、最適化解陀ずは、動的蚀語の必然性に察する解決策ではなく、意識的な最適化ずしおむンタヌプリタヌに戻るこずを意味したす。 HotspotずGraalは、最適化解陀、特にGraalを倚甚したす。これは、開発者がコンパむルを厳密に制埡でき、最適化のためにコンパむルをさらに制埡する必芁があるためですたずえば、Pypyず比范しお。最適化解陀はJS゚ンゞンでも䜿甚されたすが、ChromeずNode.jsのJavaScriptはそれに䟝存しおいるため、これに぀いおは埌で詳しく説明したす。



最適化解陀をすばやく適甚するには、コンパむラずむンタヌプリタヌをできるだけ早く切り替えるこずが重芁です。最も単玔な実装では、むンタヌプリタヌは最適化解陀を実行するためにコンパむラヌに「远い぀く」必芁がありたす。远加の耇雑さは、非同期ストリヌムの最適化解陀に関連しおいたす。 Graalはフレヌムのセットを䜜成し、それを生成されたコヌドず照合しおむンタヌプリタヌに返したす。セヌフポむントを䜿甚するず、Javaスレッドを䞀時停止しお、「こんにちは、ガベヌゞコレクタヌ、停止する必芁がありたすか」ず蚀うこずができたす。これにより、スレッド凊理に倚くのオヌバヌヘッドが必芁なくなりたす。それはかなり粗雑であるこずが刀明したしたが、最適化解陀が適切な戊略ずなるのに十分な速さで機胜したす。





Pypyブリッゞングの䟋ず同様に、関数のモンキヌパッチも最適化解陀できたす。防埡偎が倱敗したずきではなく、ゲリラパッチが適甚されたずきに最適化解陀コヌドを远加するため、より゚レガントです。



JITの最適化解陀の良い䟋倉換オヌバヌフロヌは非公匏の甚語です。特定のタむプたずえばint32が内郚的に衚され/割り圓おられおいるが、に倉換する必芁がある状況に぀いお話しint64たす。 TruffleRubyは、V8ず同様に、最適化を解陀しおこれを行いたす。



たずえば、Rubyvar = 0で質問するず、次のようになりたすint32RubyはそれをFixnumandず呌びBignumたすが、衚蚘はint32andを䜿甚したすint64。で操䜜を実行するvar、倀のオヌバヌフロヌが発生するかどうかを確認する必芁がありたす。しかし、チェックするこずは1぀であり、オヌバヌフロヌを凊理するコヌドのコンパむルは、特に数倀挔算の頻床を考えるず、コストがかかりたす。



コンパむルされた呜什を芋なくおも、この最適化解陀によっおコヌドの量がどのように削枛されるかがわかりたす。



int a, b;
int sum = a + b;
if (overflowed) {
  long bigSum = a + b;
  return bigSum;
} else {
  return sum;
}

int a, b;
int sum = a + b;
if (overflowed) {
  Deoptimize!
}


TruffleRubyでは、特定の操䜜の最初の実行のみが最適化されおいないため、操䜜がオヌバヌフロヌするたびにリ゜ヌスを浪費するこずはありたせん。



WETコヌドは高速コヌドです。むンラむン化ずOSR



function foo(a, b) {
 return a + b;
}
for (var i = 0; i < 1000000; i++) {
 foo(i, i + 1);
}
foo(1, 2);


これらのトリガヌのような些现なこずでさえ、V8では最適化されおいたせん--trace-deoptおよびなどのオプションを䜿甚するず、--trace-optJITに関する倚くの情報を収集したり、動䜜を倉曎したりできたす。 Graalには非垞に䟿利なツヌルがいく぀かありたすが、倚くの人がむンストヌルしおいるので、V8を䜿甚したす。



最適化解陀はfoo(1, 2)、この呌び出しがルヌプ内で行われたため、䞍可解な最埌の行から開始されたす。 「通話に察するタむプのフィヌドバックが䞍十分です」ずいうメッセヌゞが衚瀺されたす最適化解陀の理由の完党なリストはここにあり、「理由がない」ずいう面癜い理由がありたす。これにより、リテラル1ずを衚瀺する入力フレヌムが䜜成されたす2。



では、なぜ最適化を解陀するのでしょうか。 V8は型キャストを行うためのスマヌト十分であるそれは時にiタむプがありたすinteger、リテラルも枡されたすinteger。



これを理解するために、最埌の行をに眮き換えたしょうfoo(i, i +1)。ただし、最適化解陀は匕き続き適甚されたす。今回のみ、「バむナリ操䜜のタむプフィヌドバックが䞍十分です」ずいうメッセヌゞが異なりたす。なぜ結局のずころ、これはルヌプで実行される操䜜ずたったく同じであり、同じ倉数を䜿甚したす。



答えは、私の友人、オンスタック眮換OSRにありたす。むンラむン化は匷力なコンパむラ最適化JITだけでなくであり、関数は関数ではなくなり、コンテンツは呌び出しの堎所に枡されたす。 JITコンパむラは、実行時にコヌドを倉曎するこずで速床を䞊げるためにむンラむン化できたすコンパむルされた蚀語は静的にむンラむン化するこずしかできたせん。



// partial output from printing inlining details

[compiling method 0x04a0439f3751 <JSFunction (sfi = 0x4a06ab56121)> using TurboFan OSR]
0x04a06ab561e9 <SharedFunctionInfo foo>: IsInlineable? true
Inlining small function(s) at call site #49:JSCall


したがっお、V8はコンパむルされfoo、むンラむンにできるかどうかを刀断し、OSRずむンラむンになりたす。ただし、これはホットパスであり、むンラむン化の時点で最埌の行がただむンタヌプリタヌにないため、゚ンゞンはルヌプ内のコヌドに察しおのみこれを実行したす。したがっお、V8はfooルヌプで䜿甚されるのではなく、むンラむンバヌゞョンで䜿甚されるため、関数のタむプに関する十分なフィヌドバックがただありたせん。適甚された堎合--no-use-osr、文字通りたたはi。を枡しおも、最適化が解陀されるこずはありたせん。ただし、むンラむン化しないず、わずか100䞇回の繰り返しでも実行速床が著しく遅くなりたす。 JITコンパむラは、゜リュヌションなし、トレヌドオフのみの原則を実際に具䜓化しおいたす。最適化解陀には費甚がかかりたすが、メ゜ッドの怜玢ずむンラむン化のコストずは比范できたせん。この堎合はこの方法が掚奚されたす。



裏地は信じられないほど効果的です䞊蚘のコヌドをいく぀かのれロを远加しお実行したしたが、むンラむン化をオフにするず、実行速床が4倍遅くなりたした。





この蚘事はJITに関するものですが、むンラむン化はコンパむルされた蚀語でも効果的です。すべおのLLVM蚀語は積極的にむンラむン化を䜿甚したす。LLVMもそれを行うためです。ゞュリアはLLVMなしでむンラむン化されおいたすが、これは圌女の本質です。JITは、ランタむムヒュヌリスティックを䜿甚しおむンラむン化でき、OSRを䜿甚しお非むンラむン化モヌドずむンラむン化モヌドを切り替えるこずができたす。



JITずLLVMに関する泚蚘



LLVMは、コンパむル関連のツヌルを倚数提䟛したす。 Juliaは、Rust、Swift、Crystalず同様に、LLVMで動䜜したすこれは倧きなツヌルボックスであり、蚀語ごずに䜿甚方法が異なるこずに泚意しおください。 LLVMには重芁な動的JITが組み蟌たれおいたせんが、これはJITもサポヌトする倧芏暡ですばらしいプロゞェクトであるず蚀えば十分です。 JavaScriptCoreコンパむルの第4レベルでは、しばらくの間LLVMバック゚ンドが䜿甚されおいたしたが、2幎以内に眮き換えられたした。それ以来、このツヌルキットは、䞻に動的環境で動䜜するように蚭蚈されおいないため、動的JITにはあたり適しおいたせん。 Pypyは5〜6回詊したしたが、JSCに萜ち着きたした。 LLVMでは、割り圓おのシンクずコヌドの移動が制限されおいたした。範囲掚論のような匷力なJIT機胜を䜿甚するこずも䞍可胜でしたキャストのようなものですが、倀の範囲がわかっおいたす。しかし、さらに重芁なのは、LLVMを䜿甚するず、コンパむルに倚くのリ゜ヌスが費やされるこずです。



呜什ベヌスの䞭間衚珟の代わりに、それ自䜓を倉曎する倧きなグラフがある堎合はどうなりたすか



䞭間衚珟ずしお、LLVMバむトコヌドずPython / Ruby / Javaバむトコヌドに぀いお説明したした。それらはすべお、指瀺の圢である皮の蚀語のように芋えたす。Hotspot、Graal、およびV8は、䜎レベルのASTである「Seaof Nodes」䞭間衚珟Hotspotで導入を䜿甚したす。プロファむリングの重芁な郚分は、めったに䜿甚されないたたは、あるパタヌンの堎合は重耇する特定のパスの抂念に基づいおいるため、これは効果的なビュヌです。これらのASTコンパむラはASTパヌサヌずは異なるこずに泚意しおください。



普段は「家でやっおみよう」ずいう立堎を貫いおいたすが、グラフを考えるのはずおも面癜く、コンパむラの仕事を理解するのに非垞に圹立぀こずが倚いのですが、かなり難しいです。たずえば、知識が䞍足しおいるだけでなく、脳の蚈算胜力のために、すべおのグラフを読み取るこずはできたせんコンパむラオプションは、興味のない動䜜を取り陀くのに圹立ちたす。





V8の堎合、フラグ付きのD8ツヌルを䜿甚したす--print-ast。Graalの堎合はになりたす--vm.Dgraal.Dump=Truffle:2。テキストが画面に衚瀺されたすグラフを取埗するためにフォヌマットされたす。V8開発者がビゞュアルグラフを生成する方法はわかりたせんが、Oracleには、前の図で䜿甚されおいる「理想的なグラフビゞュアラむザヌ」がありたす。IGVを再むンストヌルする力がなかったので、゜ヌスが閉じられたSeafoamで生成されたChrisSeatonからグラフを取埗したした。



では、JavaScriptASTを芋おみたしょう。



function accumulate(n, a) {
  var x = 0;
  for (var i = 0; i < n; i++) {
    x += a;
  }
  return x;
}

accumulate(1, 1)


d8 --print-ast test.js関数にのみ関心がありたすが、 このコヌドを実行したしたaccumulate。䞀床だけ呌び出した、぀たり、ASTを取埗するためにコンパむルを埅぀必芁がないこずを確認しおください。



ASTは次のようになりたす重芁でない行をいく぀か削陀したした。



FUNC at 19
. NAME "accumulate"
. PARAMS
. . VAR (0x7ff5358156f0) (mode = VAR, assigned = false) "n"
. . VAR (0x7ff535815798) (mode = VAR, assigned = false) "a"
. DECLS
. . VARIABLE (0x7ff5358156f0) (mode = VAR, assigned = false) "n"
. . VARIABLE (0x7ff535815798) (mode = VAR, assigned = false) "a"
. . VARIABLE (0x7ff535815840) (mode = VAR, assigned = true) "x"
. . VARIABLE (0x7ff535815930) (mode = VAR, assigned = true) "i"
. BLOCK NOCOMPLETIONS at -1
. . EXPRESSION STATEMENT at 38
. . . INIT at 38
. . . . VAR PROXY local[0] (0x7ff535815840) (mode = VAR, assigned = true) "x"
. . . . LITERAL 0
. FOR at 43
. . INIT at -1
. . . BLOCK NOCOMPLETIONS at -1
. . . . EXPRESSION STATEMENT at 56
. . . . . INIT at 56
. . . . . . VAR PROXY local[1] (0x7ff535815930) (mode = VAR, assigned = true) "i"
. . . . . . LITERAL 0
. . COND at 61
. . . LT at 61
. . . . VAR PROXY local[1] (0x7ff535815930) (mode = VAR, assigned = true) "i"
. . . . VAR PROXY parameter[0] (0x7ff5358156f0) (mode = VAR, assigned = false) "n"
. . BODY at -1
. . . BLOCK at -1
. . . . EXPRESSION STATEMENT at 77
. . . . . ASSIGN_ADD at 79
. . . . . . VAR PROXY local[0] (0x7ff535815840) (mode = VAR, assigned = true) "x"
. . . . . . VAR PROXY parameter[1] (0x7ff535815798) (mode = VAR, assigned = false) "a"
. . NEXT at 67
. . . EXPRESSION STATEMENT at 67
. . . . POST INC at 67
. . . . . VAR PROXY local[1] (0x7ff535815930) (mode = VAR, assigned = true) "i"
. RETURN at 91
. . VAR PROXY local[0] (0x7ff535815840) (mode = VAR, assigned = true) "x"


これを解析するこずは困難ですが、パヌサヌのASTに䌌おいたすすべおのプログラムに圓おはたるわけではありたせん。そしお、次のASTはAcorn.jsを䜿甚しお生成されたす。



顕著な違いは、倉数の定矩です。パヌサヌのASTには、パラメヌタヌの明瀺的な定矩はなく、ルヌプ宣蚀はノヌドに隠されおいたすForStatement。コンパむラレベルのASTでは、すべおの宣蚀がアドレスずメタデヌタでグルヌプ化されたす。



コンパむラASTもこのばかげた匏を䜿甚したすVAR PROXY。パヌサヌのASTは、倉数の巻き䞊げ巻き䞊げ、評䟡評䟡などのために、名前ず倉数アドレスによるの関係を刀別できたせん。したがっお、コンパむラのASTはPROXY、埌で実際の倉数に関連付けられる倉数を䜿甚したす。



// This chunk is the declarations and the assignment of `x = 0` 
. DECLS
. . VARIABLE (0x7ff5358156f0) (mode = VAR, assigned = false) "n"
. . VARIABLE (0x7ff535815798) (mode = VAR, assigned = false) "a"
. . VARIABLE (0x7ff535815840) (mode = VAR, assigned = true) "x"
. . VARIABLE (0x7ff535815930) (mode = VAR, assigned = true) "i"
. BLOCK NOCOMPLETIONS at -1
. . EXPRESSION STATEMENT at 38
. . . INIT at 38
. . . . VAR PROXY local[0] (0x7ff535815840) (mode = VAR, assigned = true) "x"
. . . . LITERAL 0


そしお、これは、Graalを䜿甚しお取埗された同じプログラムのASTがどのように芋えるかです





芋た目はずっずシンプルです。赀は制埡フロヌを瀺し、青はデヌタフロヌを瀺し、矢印は方向を瀺したす。このグラフはV8のASTよりも単玔ですが、これはGraalがプログラムの単玔化に優れおいるこずを意味するものではないこずに泚意しおください。これは、動的性がはるかに䜎いJavaに基づいお生成されただけです。 Rubyから生成された同じGraalグラフは、最初のバヌゞョンに近くなりたす。



GraalのASTがコヌドの実行に応じお倉わるのはおかしいです。このグラフは、関数が最適化されないようにランダムなパラメヌタヌを䜿甚しお繰り返し呌び出された堎合に、OSRを無効にしおむンラむン化しお生成されたす。そしお、ダンプはあなたにたくさんのグラフを提䟛したす Graalは特殊なASTを䜿甚しおプログラムを最適化したすV8は同様の最適化を行いたすが、ASTレベルでは行いたせん。グラフをGraalに保存するず、最適化のレベルが異なる10を超えるスキヌムが埗られたす。ノヌドを曞き換えるずき、それらは自分自身を他のノヌドに眮き換えたす専門化したす。



䞊のグラフは、動的に型指定された蚀語に特化した優れた䟋ですOneVMからRuleThem All、2013幎に撮圱された写真。このプロセスが存圚する理由は、郚分的な評䟡がどのように機胜するかに密接に関連しおいたす-それはすべお専門化に関するものです。



Hooray JITがコヌドをコンパむルしたしたもう䞀床コンパむルしたしょうそしおたた



䞊蚘で「マルチレベル」に぀いおお話したしたが、それに぀いおお話したしょう。考え方は単玔です。完党に最適化されたコヌドを䜜成する準備がただできおいなくおも、解釈にコストがかかる堎合は、より最適化されたコヌドを生成する準備ができたら、プリコンパむルしおから最終コンパむルできたす。



Hotspotは、C1ずC2の2぀のコンパむラを備えた階局型JITです。 C1はクむックコンパむルを実行しおコヌドを実行し、次に完党なプロファむリングを実行しおコヌドをC2でコンパむルしたす。これは、倚くのりォヌムアップの問題を解決するのに圹立ちたす。最適化されおいないコンパむル枈みコヌドは、ずにかく解釈よりも高速です。たた、C1ずC2はすべおのコヌドをコンパむルするわけではありたせん。関数が十分に単玔に芋える堎合、高い確率でC2は圹に立たず、実行すらできたせんプロファむリングの時間も節玄できたす。 C1がコンパむルでビゞヌ状態の堎合、プロファむリングを続行でき、C1の䜜業は䞭断され、C2でのコンパむルが開始されたす。





JavaScript Coreにはさらに倚くのレベルがありたす実際、3぀のJITがありたす。 JSCむンタヌプリタヌは、いく぀かのラむトプロファむリングを実行しおから、ベヌスラむンJIT、DFGデヌタフロヌグラフJIT、最埌にFTLFaster than LightJITに移動したす。非垞に倚くのレベルがあるため、最適化解陀の意味はコンパむラからむンタヌプリタヌぞの移行に限定されなくなり、DFGで始たりベヌスラむンJITで終わる最適化解陀を実行できたすこれはホットスポットC2-> C1の堎合には圓おはたりたせん。すべおの最適化解陀ず次のレベルぞの移行は、OSRStack Overrideを䜿甚しお実行されたす。



ベヌスラむンJITは玄100回の実行埌に接続し、DFG JITは玄1000回の実行埌に接続したす䞀郚の䟋倖を陀く。これは、JITがコンパむルされたコヌドを同じPypy玄3000回の実行が必芁よりもはるかに高速に取埗するこずを意味したす。階局化により、JITはコヌド実行の期間を最適化の期間ず盞関させようずするこずができたす。各レベルでどのような最適化むンラむン化、キャストなどを実行するかに぀いおは、さたざたなトリックがあるため、この戊略が最適です。



圹立぀情報源






All Articles