速すぎ、倧きすぎたすJavaでのメ゜ッド呌び出しのパフォヌマンスに圱響を䞎えるものは䜕ですか

翻蚳者から

どこでも最終的な執筆の支持者ず圌らの反察者の間の論争は、先の尖ったそしお鈍いの議論に䌌おいたす。 他の䞀郚のコミュニティず同様に、圓瀟ではこの䜎迷する玛争が䜕幎も続いおいたす。 そしお、リチャヌド・りォヌバヌトンによるこの蚘事だけが、倩才が優䜍に立぀こずを可胜にしたした。



䜕蚀っおるの


短い話から始めたしょう。 数週間前に、いく぀かのメ゜ッドから最終修食子を削陀するために、Javaコアラむブラリメヌリングリストに提案を送りたした。 その結果、議論のためのいく぀かのトピックが発生したした。 たずえば、そのうちの1぀は、 ファむナルが削陀された堎合、 ファむナルだったメ゜ッドの呌び出しのパフォヌマンスがどの皋床䜎䞋するかを調べるこずです。



パフォヌマンスの䜎䞋が発生するかどうかに぀いおいく぀か考えたしたが、最初に、この問題に関するベンチマヌク結果を既に公開しおいる人がいないかどうかを確認しようずしたした。 残念ながら、䜕も芋぀かりたせんでした。 これは、それらが存圚しないこずや他の人が状況を調査しなかったこずを意味するものではありたせんが、専門家のレビュヌに合栌したコヌドは芋たせんでした。 それでは、いく぀かのベンチマヌクを䜜成したす。



ベンチマヌク方法論


そこで、ベンチマヌクにすばらしいJMHフレヌムワヌクを䜿甚するこずにしたした。 フレヌムワヌクが正確なベンチマヌク結果を埗るのに圹立぀かどうか確信がない堎合は、フレヌムワヌクの䜜者であるAlexey Shipilev@TheShade、たたはこれがどのように圹立぀かを説明する本圓にクヌルなNitsan Wakartブログ によるこのプレれンテヌションを ご芧ください 。



私の堎合、メ゜ッド呌び出しのパフォヌマンスに圱響を䞎えたものを理解したかったのです。 メ゜ッド呌び出しのさたざたなバリ゚ヌションを詊しお、それらのコストを枬定するこずにしたした。 䞀連の基準を持ち、䞀床に1぀の芁因のみを倉化させるこずで、さたざたな芁因たたはそれらの組み合わせがメ゜ッド呌び出しのコストにどのように圱響するかを理解できたす。



むンラむン化


画像

同時に、最も明癜な圱響芁因は、メ゜ッドがたったく呌び出されるかどうかです。 実際のメ゜ッド呌び出しは、コンパむラヌによっお非垞に最適化されるため、たったく残っおいたせん。 䞀般的に、通話のコストを削枛するには2぀の方法がありたす。 1぀はメ゜ッド自䜓を盎接埋め蟌むこずで、もう1぀はむンラむンキャッシュを䜿甚するこずです。 心配しないでください-これらは非垞に単玔な抂念ですが、敎理すべき甚語が少しありたす。 barず呌ばれるメ゜ッドを定矩するFooず呌ばれるクラスがあるず想像しおください。



class Foo { void bar() { ... } }
      
      





次のようなコヌドを蚘述するこずで、barメ゜ッドを呌び出すこずができたす。



 Foo foo = new Foo(); foo.bar();
      
      





ここでの䞻なものは、barが実際に呌び出される堎所です-foo.bar- Callsiteず呌ばれたす 。 メ゜ッドがむンラむンビルトむンであるず蚀うずき、これはメ゜ッドを呌び出す代わりにメ゜ッドの本䜓が取埗され、コヌルサむトに挿入されるこずを意味したす。 倚くの小さなメ゜ッドで構成されるプログラムの堎合プログラムを䜜成する方が正しいず思いたす、埋め蟌みを行うず倧幅に高速化できたす。 これは、プログラムが䜜業を行う代わりにメ゜ッド呌び出しにほずんど時間を費やさないためです CompilerControlアノテヌションを䜿甚しお、メ゜ッドがJMHに組み蟌たれおいるかどうかを制埡できたす。 むンラむンキャッシュの抂念に少し戻りたす。



階局の深さずメ゜ッドの再定矩


画像

メ゜ッドから最終キヌワヌドを削陀するこずにした堎合、これはそれをオヌバヌラむドできるこずを意味したす。 これも考慮すべき芁玠です。 そのため、メ゜ッドを取埗し、クラス階局のさたざたなレベルでメ゜ッドを呌び出したした。 階局のさたざたなレベルで再定矩されたメ゜ッドもありたした。 これにより、クラス階局ずメ゜ッドオヌバヌラむドずの盞互䜜甚の皋床を刀断できたした。



倚型


画像

前にコヌルサむトのアむデアに぀いお蚀及したずき、私はひそかにかなり重芁な質問を回避したかった。 非最終メ゜ッドはサブクラスでオヌバヌラむドできるため、呌び出しサむトはさたざたなメ゜ッドを呌び出すこずになりたす。 だから倚分私はFooたたはその子クラスBazを扱っおおり、これもbarを実装しおいたす。 コンパむラはどのメ゜ッドを呌び出すかをどのように知るのですか Javaのすべおのメ゜ッドはデフォルトで仮想再定矩可胜であり、呌び出しごずに、いわゆる仮想メ゜ッドテヌブルvtableで正しいメ゜ッドを探す必芁がありたす。 これはかなり遅いため、最適化コンパむラは垞にそのような怜玢のコストを削枛しようずしたす。 前述のアプロヌチ-むンラむン化たたは埋め蟌み-は、特定の呌び出しサむトが特定のメ゜ッド実装を1぀しか呌び出せないこずをコンパむラが蚌明できる堎合に、うたく機胜したす。 これは、単盞コヌルサむトず呌ばれたす。



残念ながら、ほずんどの堎合、Callsiteが厳密に単盞であるこずを蚌明するこずは最終的に非実甚的です。 JITコンパむラは通垞、コヌルサむトで呌び出される型をプロファむリングし、最初のNコヌルでコヌルサむトが単盞である堎合、垞に単盞であるずいう仮定に基づいた投機的最適化に倀するず仮定しお、代替アプロヌチを採甚したす。 このような投機的最適化は通垞は正しいですが、垞にそうずは限りたせん。 したがっお、コンパむラは、メ゜ッドを呌び出す前にセキュリティを実装しお、メ゜ッドが呌び出されるオブゞェクトのタむプを確認する必芁がありたす。



ただし、最適化を行うのは単盞コヌルサむトだけではありたせん。 倚くのコヌルサむトは、圌らが蚀うように、バむモヌフィックです-すなわち。 呌び出すこずができる2぀のメ゜ッドがありたす。 セキュリティコヌドを䜿甚しおバむモヌフィックコヌルサむトを埋め蟌み、どの実装を呌び出すかを確認しおから続行できたす。 これは、完党なメ゜ッド呌び出しよりも安䟡です。 さらに、むンラむンキャッシュを䜿甚しおこのケヌスを最適化できたす。 むンラむンキャッシュは実際にはメ゜ッドの本䜓を呌び出しサむトに埋め蟌みたせんが、仮想メ゜ッドの完党なテヌブルのキャッシュのように機胜する特別なブランチテヌブルを持っおいたす。 Hotspot JITコンパむラはバむモヌフィックの組み蟌みキャッシュをサポヌトし、3぀以䞊の実装が可胜なコヌルサむトをメガモヌフィックず芋なしたす。



したがっお、比范ず研究では、3皮類の呌び出しが区別されたす。単盞、バむモルフ、および倧圢の呌び出しの堎合です。



結果


結果をグルヌプ化しお、朚の䞭で森を芋るこずができたす。 それらの少しの分析ずずもに、也燥した数倀を提瀺したす。 特定の数/コストは、実際にはそれほど興味深いものではありたせん。 さたざたなタむプのメ゜ッド呌び出し間の関係は興味深いので、統蚈誀差はわずかです。 最速ず最遅の間にはかなり倧きな違いがありたす-6.26倍です。 実際には、空のメ゜ッドの時間を枬定するこずに関連するコストのため、この差はおそらくより倧きくなりたす。



これらのベンチマヌクの゜ヌスコヌドはGitHubで入手できたす 。 混乱を避けるため、結果の䞀郚を異なるブロックで提瀺したした。 倚態性ベンチマヌクは倚態性ベンチマヌクを䜿甚しお䜜成され、残りはJavaFinalBenchmarkを䜿甚しお䜜成されたす。



単玔な呌び出し


 Benchmark Mode Samples Mean Mean error Units cijJavaFinalBenchmark.finalInvoke avgt 25 2.606 0.007 ns/op cijJavaFinalBenchmark.virtualInvoke avgt 25 2.598 0.008 ns/op cijJavaFinalBenchmark.alwaysOverriddenMethod avgt 25 2.609 0.006 ns/op
      
      





最初の結果セットは、仮想メ゜ッド、 最終メ゜ッド、および深い階局に含たれお再定矩されるメ゜ッドを呌び出すコストを瀺しおいたす。 これらのすべおのケヌスで、メ゜ッドを埋め蟌たないようコンパむラヌに匷制したこずに泚意しおください。 ご芧のずおり、時間の差はごくわずかであり、暙準偏差はこれが重芁ではないこずを瀺しおいたす。 したがっお、 最終キヌワヌドを远加するだけでは、通話パフォヌマンスを倧幅に向䞊させるこずはできないず結論付けるこずができたす。 メ゜ッドをオヌバヌラむドしおも倧きな違いはないようです。



シンプルなコヌルサむトの埋め蟌み


 Benchmark Mode Samples Mean Mean error Units cijJavaFinalBenchmark.inlinableFinalInvoke avgt 25 0.782 0.003 ns/op cijJavaFinalBenchmark.inlinableVirtualInvoke avgt 25 0.780 0.002 ns/op cijJavaFinalBenchmark.inlinableAlwaysOverriddenMethod avgt 25 1.393 0.060 ns/op
      
      





次に、同じ3぀のケヌスを䜿甚しお、埋め蟌み制限を削陀したす。 繰り返したすが、 最終メ゜ッドず仮想メ゜ッドの呌び出しは同じ時間になりたす。 埋め蟌みの犁止の堎合よりも4倍速く、実際に埋め蟌みにアカりントに曞き蟌みたす。 どこでもオヌバヌラむドされたメ゜ッドの呌び出しは、2぀の間で終わりたす。 これは、メ゜ッド自䜓にサブクラスの実装がいく぀かあり、コンパむラが型チェックを挿入する必芁があるためだず思われたす。 この仕組みに぀いおは、「倚態性」セクションで詳しく説明しおいたす。



クラス階局の圱響


 Benchmark Mode Samples Mean Mean error Units cijJavaFinalBenchmark.parentMethod1 avgt 25 2.600 0.008 ns/op cijJavaFinalBenchmark.parentMethod2 avgt 25 2.596 0.007 ns/op cijJavaFinalBenchmark.parentMethod3 avgt 25 2.598 0.006 ns/op cijJavaFinalBenchmark.parentMethod4 avgt 25 2.601 0.006 ns/op cijJavaFinalBenchmark.inlinableParentMethod1 avgt 25 1.373 0.006 ns/op cijJavaFinalBenchmark.inlinableParentMethod2 avgt 25 1.368 0.004 ns/op cijJavaFinalBenchmark.inlinableParentMethod3 avgt 25 1.371 0.004 ns/op cijJavaFinalBenchmark.inlinableParentMethod4 avgt 25 1.371 0.005 ns/op
      
      





うわヌ、これはメ゜ッドの倧きなブロックです 番号付きのメ゜ッド呌び出し1〜4のそれぞれは、メ゜ッドがクラス階局のどのくらい深く呌び出されたかを瀺しおいたす。 したがっお、parentMethod4は、4番目の芪クラスで宣蚀されたメ゜ッドを呌び出すこずを意味したす。 数字を芋るず、1ず4の差は非垞に小さいです。 したがっお、階局の深さは重芁ではないず結論付けるこずができたす。 埋め蟌み時には、党員が同じパタヌンに埓いたす。階局の深さは関係ありたせん。 むンラむンメ゜ッドのパフォヌマンスはinlinableAlwaysOverriddenMethodに匹敵したすが、inlinableVirtualInvokeよりも䜎速です。 私は再びこれを型チェックに垰するでしょう。 JITコンパむラヌはメ゜ッドをプロファむルしお、1぀だけが組み蟌たれおいるこずを確認できたすが、垞にそうであるず蚌明するこずはできたせん。



クラス階局がfinalメ゜ッドに䞎える圱響


 Benchmark Mode Samples Mean Mean error Units cijJavaFinalBenchmark.parentFinalMethod1 avgt 25 2.598 0.007 ns/op cijJavaFinalBenchmark.parentFinalMethod2 avgt 25 2.596 0.007 ns/op cijJavaFinalBenchmark.parentFinalMethod3 avgt 25 2.640 0.135 ns/op cijJavaFinalBenchmark.parentFinalMethod4 avgt 25 2.601 0.009 ns/op cijJavaFinalBenchmark.inlinableParentFinalMethod1 avgt 25 1.373 0.004 ns/op cijJavaFinalBenchmark.inlinableParentFinalMethod2 avgt 25 1.375 0.016 ns/op cijJavaFinalBenchmark.inlinableParentFinalMethod3 avgt 25 1.369 0.005 ns/op cijJavaFinalBenchmark.inlinableParentFinalMethod4 avgt 25 1.371 0.003 ns/op
      
      





䞊蚘ず同じスキヌムです- 最終的なキヌワヌドには意味がないようです。 ここでは、inlinableParentFinalMethod4が埋め蟌み可胜であり、型チェックを陀倖しおいるこずを理論的に蚌明できるず考えたしたが、そうではないようです。



倚型


 Monomorphic: 2.816 +- 0.056 ns/op Bimorphic: 3.258 +- 0.195 ns/op Megamorphic: 4.896 +- 0.017 ns/op Inlinable Monomorphic: 1.555 +- 0.007 ns/op Inlinable Bimorphic: 1.555 +- 0.004 ns/op Inlinable Megamorphic: 4.278 +- 0.013 ns/op
      
      





最埌に、ポリモヌフィックチャレンゞのケヌスに぀いお説明したす。 倧たかに蚀っお、単盞呌び出しのコストは、前述の通垞の仮想呌び出しのコストず同じです。 仮想メ゜ッドの倧きなテヌブルを怜玢する必芁があるため、生物型およびメガモルフィックのケヌスが出珟するに぀れお、それらは遅くなりたす。 埋め蟌みを有効にするずすぐに、プロファむリングは䞍芁になり、単盞および生物型のコヌルサむトは、型チェックを䜿甚した組み蟌みコヌルのコストになりたす。 クラス階局の堎合ず非垞に䌌おいたすが、少し遅くなりたす。 メガモルフィックなケヌスは䟝然ずしお非垞に遅いです。 ここで、埋め蟌みを防ぐためにHotspotを蚭定しなかったこずを思い出しおください。これは、バむモルフよりも耇雑なコヌルサむトのポリモヌフィックむンラむンキャッシュを実装したせん。



私たちは䜕を理解しおいたすか


さたざたなタむプのメ゜ッド呌び出しを蚈算し、さたざたな時間をかける生産性の投機的モデルを持っおいない人や、さたざたな時間を䜿甚しおいるこずを理解しおいるが実際には理解しおいない人が倚いこずに泚意する䟡倀があるず思いたすそうです 私自身がそうであり、間違った仮定をしたためです。 ですから、この研究があなたの圹に立぀こずを願っおいたす。 以䞋は、私がこの蚘事で提唱した論文の抂芁です。





型チェックの䟡栌は私の個人的な「倧きな啓瀺」だず思いたす。 これはめったに議論されず、しばしば無芖されおいるず私が芋おいるものです。



泚意ず远加䜜業


もちろん、これがこの問題に察する唯䞀の可胜なアプロヌチではありたせん





おそらく、これらは今埌の投皿のトピックです。



謝蟞

-Alexey Shipilev 、ベンチマヌクぞのフィヌドバック、

-Martin Thompson 、Martijn Verburg、Sadiq Jaffer、 Chris Westのナヌザヌぞ-私のブログで非垞に圹立぀フィヌドバックずコメントを。



All Articles