「大きなうそがあり、小さなうそがあり、Javaパフォーマンス統計があります。」
最近、私は驚くほど信じられないほどのテスト結果の範囲を持つ豊富なJavaパフォーマンステストに気付き始めました。 誰がテストを実行するかによって、結果はまったく逆になります。
そのようなテストがマーケティングの一部であることを多くの人が理解しています。 そして、それを注文する人も結果を注文します。 しかし、状況の驚くべきことは、嘘を非難する人はいないということです。 JavaがC#に比べて1桁劣るテストの真実も、JavaがC ++(Cではなく)より優れているテストも否定できません。 問題は、そのようなテストが主に初心者開発者を対象としており、より多くのプログラマーをキャンプに誘い込み、それによってビジネス向けプラットフォームの商業的魅力を高めることです。 そして、そのようなテストでは、初心者のプログラマーがめったに入らないHotSpotの動的JITコンパイルと適応最適化の詳細が意図的に見落とされています。
これは何でいっぱいですか? 一般的な認知的不協和。
また、DelphiとJavaで比較テストプログラムを作成しました。 そして、Javaのパフォーマンスがほぼ10倍遅れました。 もちろん、あなたは曲がった手にすべてを責めることができたので、私はJITコンパイルメカニズムを理解しようとしました。 結局のところ、物事が手に入らず、実際にJavaがそのような非効率的なネイティブコードを作成する場合、そのようなプラットフォームから可能な限り離れた場所に留まる必要があります。 しかし、現実には、これは最も一般的な産業用プログラミング言語の1つであり、企業はそのようなパフォーマンスの低下に耐えることはできません。
Javaの腸で起こっていることの本質をどのように理解したかを指で説明しようと思います。何か間違えた場合は、訂正してください。 持続性にもかかわらず、いくつかのプロセスの本質は私にとって謎のままです。
そもそも、適応型JITコンパイルはかなりオーバーヘッドの多い手順であり、すべてのコードを一度にコンパイルするとコストがかかりすぎます。 はい、これは必要ありません。 産業用アプリケーションの統計によると、プログラムの80%がコードの20%に費やされています。 コンパイルするのに最も有益なのはこれらの20%です。 コードの残りの部分は、解釈モードで実行されてもパフォーマンスに大きな影響はありません。 これらの20%を計算するには、アルゴリズムを徹底的に研究する必要があります。 また、すべてのHotSpotアルゴリズムを学習するには、重要な統計が必要なので、最初はプログラム全体が解釈モードで動作します。
統計が蓄積されると、コンパイラーはアプリケーションを部分的にコンパイルします。 最小コンパイル単位は関数です。 クライアント構成では、JITコンパイラーが動作を開始する前に、統計を収集するために1,500個の関数呼び出しが割り当てられます。 より積極的な最適化が実行されるサーバー構成の場合、15,000回の呼び出しが必要です。 これは、統計を収集し、投機的なコード最適化を行うのに十分なはずです。これにより、高いコードスループットを実現できます。 負荷の高いシステムでは、統計は即座に収集されますが、これはデスクトッププログラムについては言えません。
最適化の過程で、コンパイラはコードの機能に関する仮説を立て、統計を分析し、その仮定を証明または反証しようとします。 HotSpotがその仮説を反証した場合、JITコンパイラーは、すでにコンパイルされたコードの後続の再コンパイルのために最適化を解除します。 そして、新しい仮説に従って新しい最適化を実行します。 ほとんどの場合、HotSpotは標準の設計パターンに従ってアルゴリズムの構造を仮定します。 それが、すべてのJavaプログラマーができるだけ頻繁に設計パターンを使用するように教えられている理由です。
オリジナルの効果的なアルゴリズムを考え出すことができたとしても、これはプログラムがより速く実行されるという意味ではなく、逆です。 HotSpotは、コードに関する誤った仮説を頻繁に提示し、アルゴリズムをコンパイルする最適な方法が見つかるまで最適化解除と再コンパイルを実行します。 また、非標準アルゴリズムのJIT最適化が、かさばるパターンの最適化よりも優れているということはありません。 SUNのエンジニアはかつて、Javaにパターンを効果的に機能させることを教えるために素晴らしい仕事をしていました。
一般的な最適化の中で、HotSpotは仮想関数への遅い呼び出しをインラインルックアップに変換できます。 ただし、このために、HotSpotはプログラム内のすべての多相変換について知る必要があります。 特定の関数が最も頻繁に呼び出される場所を把握しているため、コンパイラはその関数を使用される場所のできるだけ近くに移動します。 Cでも、インラインコールを手動で管理することにより、同様のレベルのパフォーマンスを実現できます。 しかし、C ++でオブジェクト指向のパラダイムを使用する場合、これは困難であり、時には不可能です。 したがって、HotSpotは十分な量の統計情報を収集することで、Cのパフォーマンスに近づき、C ++を上回ることができます。 そして、パターンを使用する場合、HotSpotはそれらを認識し、必要な置換を最初に実行します。 ただし、非標準のアルゴリズムを使用している場合、この場合、実行された置換に関するHotSpot仮説は、より高い確率で偽であることが判明します。 より効率的な最適化のために新しい統計が収集されますが、JITコンパイラーは厳密に指定された反復間隔で開始します。
前述のように、デフォルトでは、これはデスクトッププログラムの場合は1,500回の反復です。 これは大したことのように思えるかもしれません。 この場合、HotSpotの動作は-XX:CompileThresholdパラメーターを使用して変更できます。 これにより、HotSpotのウォームアップに必要な時間を短縮できます。 ただし、ウォームアップ時間を短縮しても、統計の収集にかかる時間は短縮されません。 収集された統計は、プログラムのアクティビティから取得されます。 したがって、値-XX:CompileThreshold = 1の設定が小さすぎると、JITコンパイラーがアイドル状態になる頻度が高くなります。特に開始後、新しい統計が連続して到着する場合のみです。 これにより、パフォーマンス統計も歪められます。 最新のプロセッサパワーでは、最適値は-XX:CompileThreshold = 100です。 しかし、楽しみのために、このパラメーターはアルゴリズムに非常に敏感なので、このパラメーターを試してみることをお勧めします。 JITコンパイラの動作を監視するには、コンソールで-XX:+ PrintCompilationパラメーターを使用します。 これは非常に有益な観察になるでしょう。
したがって、パフォーマンステストの結果は、HotSpotがアルゴリズムに関する統計を収集した時間に依存します。 統計がない場合、JITコンパイルはありません。 Java統計を測定するときは、インタープリターのパフォーマンス、最適化されていないコードパフォーマンス、最適化されたコードパフォーマンスなど、測定対象を明確に理解する必要があります。 そしてそれは冗談のようになります-「彼らがどのように投票したかは関係ありません、彼らが数えた方法は重要です。」
エピローグの代わりに。
そのようなアーキテクチャを利点または欠点と見なすことはできますか? 誰もが個人的にこの質問に答え、誰もが正しいでしょう。 Javaは、それに割り当てられたすべてのタスクを解決できますが、それを行う方法を知る必要があり、場合によっては何らかの方法で妥協する必要があります。 しかし、創造の喜びが何であるかを誰も知らないプログラマーは知っています。 プログラマーの仕事の最も強力な動機は、彼の仕事の仕事を熟考する喜びと満足です。 したがって、雇用者がお金で私たちをやる気にさせることはとても困難です。 [1]私たち(少なくとも私たちのほとんど)は、私たちの仕事が大好きです。仕事の結果が喜びをもたらし、同時に、楽しいボーナスとして、まだ十分に支払われているからです。 そのため、私たちはお客様、同僚、コミュニティとの幸せと創造的な関係の構築を支援するツールを愛しています。 あなたの仕事から喜びを得て、仕事で幸せになってください。あなたは自分に何かを強制する方法を考える必要はありません。 人は無意識のうちにポジティブな感情を求めて努力し、ネガティブな感情を避けます。そのため、あなたの仕事にポジティブな創造的関係を築き、あなた自身の手が働きたいと思うでしょう。 仕事から喜びを得られないなら、あなたはすべきではない何かをしているのです。 あなたを本当に幸せにすること、あなたが本当にできることをしてください-世界をより良い場所にしてください。
幸せに。 コーヒーを飲む。 Javaを記述します。
UPD:タイプミスを修正。 テキストを編集してくれたZeijに感謝します。