Javaで正しいマイクロベンチマークをどのように作成(および実行)しますか?
考えるべきさまざまなことを示すコードサンプルとコメントを探しています。
例:ベンチマークは、時間/反復または反復/時間を測定する必要がありますか?その理由は?
Javaで正しいマイクロベンチマークをどのように作成(および実行)しますか?
考えるべきさまざまなことを示すコードサンプルとコメントを探しています。
例:ベンチマークは、時間/反復または反復/時間を測定する必要がありますか?その理由は?
回答:
Java HotSpotの作成者からのマイクロベンチマークの作成に関するヒント:
ルール0: JVMとマイクロベンチマークに関する信頼できる論文を読んでください。良いものは、ブライアン・ゲッツ、2005年です。マイクロベンチマークにあまり期待しないでください。限られた範囲のJVMパフォーマンス特性のみを測定します。
ルール1:常に、テストカーネルを実行するウォームアップフェーズを含めます。これは、タイミングフェーズの前にすべての初期化とコンパイルをトリガーするのに十分です。(ウォームアップフェーズでは反復回数を減らしても問題ありません。経験則では、数万回の内部ループの反復回数です。)
ルール2:常にで実行-XX:+PrintCompilation
、-verbose:gc
あなたはコンパイラやJVMの他の部分は、あなたのタイミング位相中に予期しない仕事をしていないことを確認することができますので、など。
ルール2.1:タイミングフェーズとウォームアップフェーズの最初と最後にメッセージを出力するので、タイミングフェーズ中にルール2からの出力がないことを確認できます。
ルール3:-client
と-server
、およびOSRと通常のコンパイルの違いに注意してください。この-XX:+PrintCompilation
フラグは、アットマーク付きのOSRコンパイルを報告して、非初期エントリポイントを示しますTrouble$1::run @ 2 (41 bytes)
。次に例を示します。最高のパフォーマンスを求めている場合は、クライアントよりサーバーを優先し、OSRよりも定期的に実行します。
ルール4:初期化の影響に注意してください。印刷するとクラスがロードおよび初期化されるため、タイミングフェーズで初めて印刷しないでください。クラスのロードを具体的にテストする場合(およびその場合はテストクラスのみをロードする場合)を除き、ウォームアップフェーズ(または最終レポートフェーズ)の外で新しいクラスをロードしないでください。ルール2は、そのような影響に対する防御の最前線です。
ルール5:最適化解除と再コンパイルの影響に注意してください。タイミングフェーズでコードパスを初めて使用しないでください。パスがまったく使用されないという以前の楽観的な仮定に基づいて、コンパイラがコードをジャンクして再コンパイルする場合があるためです。ルール2は、そのような影響に対する防御の最前線です。
ルール6:適切なツールを使用してコンパイラーの心を読み、コンパイラーが生成するコードに驚かされることを期待します。何かを速くしたり遅くしたりするものについての理論を形成する前に、自分でコードを調べてください。
ルール7:測定のノイズを減らします。ベンチマークを静かなマシンで実行し、それを数回実行して、外れ値を破棄します。-Xbatch
コンパイラーをアプリケーションとシリアル化するために使用し、コンパイラー-XX:CICompilerCount=1
がそれ自体と並行して実行されないように設定することを検討してください。GCのオーバーヘッド、セット減らすために最善を試してみてくださいXmx
(十分な大きさ)に等しくXms
し、使用しUseEpsilonGC
、それが利用可能な場合。
ルール8:おそらくより効率的であり、この唯一の目的のためにすでにデバッグされているため、ベンチマークにはライブラリを使用します。などJMH、キャリパーやビルやJava用ポールの優れたUCSDベンチマーク。
System.nanoTime()
は、よりも正確であることが保証されていないことに注意してくださいSystem.currentTimeMillis()
。少なくとも正確であることが保証されているだけです。ただし、通常はかなり正確です。
System.nanoTime()
代わりに使用しなければならない主な理由System.currentTimeMillis()
は、前者が単調に増加することが保証されているからです。2つのcurrentTimeMillis
呼び出しで返された値を減算すると、システム時間が一部のNTPデーモンによって調整されたために、実際には否定的な結果が得られる可能性があります。
この質問に回答済みのマークが付いていることは知っていますが、マイクロベンチマークの作成に役立つ2つのライブラリについて言及したいと思います
入門チュートリアル
入門チュートリアル
Javaベンチマークの重要な点は次のとおりです。
System.gc()
イテレーション間で呼び出すことはできませんが、テスト間で実行することをお勧めします。これにより、各テストで「クリーンな」メモリスペースを使用できるようになります。(はい、gc()
保証というよりヒントですが、私の経験では、ガベージコレクションが実際に行われる可能性が非常に高いです。)私は.NETのベンチマークフレームワークの設計についてブログを書いているところです。私が持っているカップルの以前の記事あなたにいくつかのアイデアを与えることができるかもしれ-ませんすべてはもちろん、適切であろうが、それの一部であってもよいです。
gc
、常に未使用のメモリが解放されますが。
System.gc()
、前のテストで作成されたオブジェクトが原因で、1つのテストでガベージコレクションを最小限に抑えることをどのように提案しますか?私は実用的で、独断的ではありません。
jmhは最近OpenJDKに追加されたものであり、Oracleの一部のパフォーマンスエンジニアによって記述されています。確かに一見の価値があります。
jmhは、Javaおよびその他のJVMをターゲットとする言語で記述されたnano / micro / macroベンチマークを構築、実行、分析するためのJavaハーネスです。
サンプルテストのコメントに埋め込まれた非常に興味深い情報。
以下も参照してください。
2つのアルゴリズムを比較する場合は、順序を入れ替えて、それぞれに少なくとも2つのベンチマークを実行します。つまり:
for(i=1..n)
alg1();
for(i=1..n)
alg2();
for(i=1..n)
alg2();
for(i=1..n)
alg1();
異なるパスで同じアルゴリズムのランタイムにいくつかの顕著な違い(5-10%時々)を発見しました。
また、各ループの実行時間が少なくとも10秒程度になるように、nが非常に大きいことを確認してください。反復が多いほど、ベンチマーク時間の数値が大きくなり、データの信頼性が高くなります。
ベンチマークされたコードで計算された結果を何らかの方法で使用していることを確認してください。それ以外の場合は、コードを最適化することができます。
Javaでマイクロベンチマークを記述する場合、多くの落とし穴があります。
最初に、多かれ少なかれランダムな時間がかかるすべての種類のイベントで計算する必要があります:ガベージコレクション、キャッシュ効果(ファイルのOSとメモリのCPUの)、IOなど。
2番目:非常に短い間隔で測定された時間の正確さは信頼できません。
3番目:JVMは実行中にコードを最適化します。したがって、同じJVMインスタンスでの異なる実行は、ますます速くなります。
私の推奨事項:ベンチマークを数秒実行します。これは、ミリ秒を超えるランタイムよりも信頼性が高くなります。JVMをウォームアップします(JVMが最適化を実行できることを測定せずにベンチマークを少なくとも1回実行することを意味します)。そして、ベンチマークを複数回(おそらく5回)実行し、中央値を取得します。新しいJVMインスタンスですべてのマイクロベンチマークを実行します(すべてのベンチマークの新しいJavaを呼び出します)。そうしないと、JVMの最適化の影響が後で実行されるテストに影響を与える可能性があります。ウォームアップフェーズでは実行されないものを実行しないでください(クラスロードと再コンパイルがトリガーされる可能性があるため)。
また、異なる実装を比較する場合は、マイクロベンチマークの結果を分析することが重要になる場合があることにも注意してください。したがって、有意性検定行う必要があります。
これはA
、ベンチマークのほとんどの実行中に、実装よりも実装の方が高速になる可能性があるためB
です。ただしA
、スプレッドも大きい可能性があるため、の測定されたパフォーマンス上の利点はA
、B
た。
したがって、マイクロベンチマークを正しく記述して実行することも重要ですが、正しく分析することも重要です。
他の優れたアドバイスに加えて、私は次のことにも注意します。
一部のCPU(TurboBoostを搭載したIntel Core i5範囲など)では、温度(および現在使用されているコアの数、および使用率)がクロック速度に影響します。CPUは動的にクロックされるため、結果に影響を与える可能性があります。たとえば、シングルスレッドアプリケーションの場合、最大クロック速度(TurboBoostを使用)は、すべてのコアを使用するアプリケーションよりも高速です。したがって、これは一部のシステムでのシングルスレッドとマルチスレッドのパフォーマンスの比較を妨げる可能性があります。温度とボラタジはターボ周波数が維持される期間にも影響することに注意してください。
おそらく、あなたが直接制御できる、より根本的に重要な側面です。正しいものを測定していることを確認してください!たとえばSystem.nanoTime()
、特定のコードのベンチマークに使用している場合は、不要なものを測定しないように、意味のある場所に割り当ての呼び出しを配置します。たとえば、次のことは行わないでください。
long startTime = System.nanoTime();
//code here...
System.out.println("Code took "+(System.nanoTime()-startTime)+"nano seconds");
問題は、コードが終了したときにすぐに終了時刻を取得していないことです。代わりに、以下を試してください。
final long endTime, startTime = System.nanoTime();
//code here...
endTime = System.nanoTime();
System.out.println("Code took "+(endTime-startTime)+"nano seconds");
println
、個別のヘッダー行などではなく、その呼び出しの文字列引数を構築する最初のステップSystem.nanoTime()
として評価する必要があります。コンパイラーが最初のものでできることは2番目のものではできないことは何もありませんし、停止時間を記録する前に余分な作業をするように奨励することもできません。
http://opt.sourceforge.net/ Java Micro Benchmark-異なるプラットフォーム上のコンピューターシステムの比較パフォーマンス特性を決定するために必要な制御タスク。最適化の決定を導き、さまざまなJava実装を比較するために使用できます。