759

どのようにしてJavaで正しいマイクロベンチマークを書いて(そして実行して)いますか?

さまざまなことを説明するためのコードサンプルとコメントをここで探しています。

例:ベンチマークでは時間/反復または反復/時間を測定する必要がありますが、その理由は何ですか。

関連するストップウォッチのベンチマークは可能ですか?


  • 関連情報については、数分前の[この質問] [1]を参照してください。編集:申し訳ありませんが、これは答えになるはずはありません。コメントとして投稿する必要があります。 [1]:stackoverflow.com/questions/503877/… - Tiago
  • この質問のポスターをこのような質問に参照することを計画した後、この質問が存在しないことに気付きました。だからここでそれは、うまくいけばそれは時間をかけていくつかの良いヒントをまとめるでしょう。 - John Nilsson
  • Java 9はマイクロベンチマークのためのいくつかの機能を提供するかもしれません:openjdk.java.net/jeps/230 - Raedwald
  • @ Raedwald JEPはJDKコードにマイクロベンチマークを追加することを目的としていると思いますが、jmhがJDKに含まれるとは思わない... - assylias
  • おそらく複製:Javaでメソッドの実行を計測するにはどうすればよいですか。 - Basil Bourque

11 답변


701

マイクロベンチマークを書くためのヒントJava HotSpotの作成者から

規則0:JVMとマイクロベンチマークに関する評判の良い論文を読んでください。良いものは2005年ブライアン・ゲッツ。マイクロベンチマークからあまり期待しないでください。それらは限られた範囲のJVMパフォーマンス特性のみを測定します。

規則1:タイミングフェーズの前にすべての初期化とコンパイルをトリガーするのに十分な、テストカーネルを最後まで実行するウォームアップフェーズを必ず含めてください。 (ウォームアップ段階での反復回数は少なくても大丈夫です。経験則では、数万回の内部ループ反復が行われます。)

規則2:常に実行-XX:+PrintCompilation-verbose:gcそのため、タイミング段階でコンパイラやJVMの他の部分が予期しない動作をしていないことを確認できます。

規則2.1タイミングフェーズとウォームアップフェーズの最初と最後にメッセージを印刷するので、タイミングフェーズ中にルール2からの出力がないことを確認できます。

規則3:-clientと-server、およびOSRと通常のコンパイルの違いに注意してください。の-XX:+PrintCompilationflagは、初期ではないエントリポイントを示すために、アットマークを付けて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ベンチマーク


  • これも興味深い記事でした。ibm.com/developerworks/java/library/j-jtp12214 - John Nilsson
  • また、±15ミリ秒の精度で問題ない場合は、System.currentTimeMillis()を使用しないでください。これは、ほとんどのOS + JVMの組み合わせで一般的です。代わりにSystem.nanoTime()を使用してください。 - Scott Carey
  • javaOneからのいくつかの論文:azulsystems.com/events/javaone_2009/session/… - bestsss
  • 注意すべきことSystem.nanoTime()ではない保証されたより正確にSystem.currentTimeMillis()。それは少なくとも同じくらい正確であることだけが保証されています。しかし、通常はかなり正確です。 - Gravity
  • 使用しなければならない主な理由System.nanoTime()の代わりにSystem.currentTimeMillis()前者は単調に増加することが保証されているということです。 2を返した値を減算currentTimeMillisおそらくシステム時間がNTPデーモンによって調整されたためである。 - Waldheinz

221

私はこの質問が回答済みとしてマークされていることを知っていますが、私たちがマイクロベンチマークを書くことを可能にする2つのライブラリに言及したいと思いました

Googleのキャリパー

チュートリアルを始める

  1. http://codingjunkie.net/micro-benchmarking-with-caliper/
  2. http://vertexlabs.co.uk/blog/caliper

OpenJDKからのJMH

チュートリアルを始める

  1. JVMでベンチマークの落とし穴を避ける
  2. http://nitschinger.at/Using-JMH-for-Java-Microbenchmarking
  3. http://java-performance.info/jmh/


  • +1それは受け入れられた答えのルール8として追加された可能性があります。ルール8:非常に多くのことがうまくいかないことがあるので、あなたはそれを自分でやろうとするよりもおそらく既存のライブラリを使うべきです! - assylias
  • @Pangea jmhはおそらく最近Caliperより優れています。groups.google.com/forum/#!msg/mechanical-sympathy/m4opvy4xq3U/… - assylias

78

Javaベンチマークで重要なことは以下のとおりです。

  • タイミングを合わせる前に、コードを数回実行して、最初にJITをウォームアップします。
  • 結果を数秒または(より良い)数十秒で測定できるように十分長い時間実行してください。
  • 電話できない間System.gc()繰り返しの間は、テストの間に実行することをお勧めします。そうすることで、各テストでうまく動作するように「クリーンな」メモリスペースを確保できるようになります。 (はい、gc()保証というよりはヒントですが、非常にありそうなそれは私の経験では本当にガベージコレクションになるでしょう。)
  • 私は、反復と時間、そして「最良の」アルゴリズムが1.0の得点を得るように調整できる時間/反復の得点を表示するのが好きです。これはあなたが走ることができることを意味しますすべて最も長い時間のアルゴリズムで、反復回数と時間の両方が異なりますが、それでも同等の結果が得られます。

私はちょうど.NETのベンチマークフレームワークの設計についてブログを書いているところです。私は持っていますカップル以前の投稿それはあなたにいくつかのアイデアを与えることができるかもしれません - もちろんすべてが適切であるというわけではありませんが、それのいくつかはそうかもしれません。


  • マイナーニックピック:IMO"各テストが"を受けるようにする"各テストが"取得されるように"にする必要があります。というのも前者は電話をかけるという印象を与えるからです。gc 常に未使用のメモリを解放します。 - Sanjay T. Sharma
  • @ SanjayT.Sharma:まあ、意図それは実際にそうであるということです。厳密には保証されていませんが、実際にはかなり強力なヒントです。より明確になるように編集します。 - Jon Skeet
  • System.gc()の呼び出しに同意しません。ヒントです、これがすべてです。 「うまくいけば何かをするだろう」ということすらありません。あなたは決してそれを呼ぶべきではありません。これはプログラミングであり、芸術ではありません。 - gyorgyabraham
  • @gyabraham:はい、それはヒントです - しかし、それは私が通常取られるのを観察したものです。そのため、使用したくない場合はSystem.gc()、以前のテストで作成されたオブジェクトが原因で、1回のテストでガベージコレクションを最小限に抑えることをどのように提案しますか。私は実用的で、独断的ではありません。 - Jon Skeet
  • @gyabraham:「素晴らしい後退」とはどういう意味かわかりません。もう少し詳しく説明できますか。より良い結果を得るための提案はありますか。これは保証ではないと明示的に述べています... - Jon Skeet

43

jmhは最近OpenJDKに追加されたもので、オラクルの一部のパフォーマンスエンジニアによって書かれました。確かに一見の価値があります。

jmhは、JavaおよびJVMをターゲットとする他の言語で記述されたナノ/マイクロ/マクロのベンチマークを構築、実行、および分析するためのJavaハーネスです。

非常に興味深い情報が埋められていますサンプルテストのコメント

また見なさい:



17

ベンチマークは時間/反復または反復/時間を測定する必要がありますか、そしてその理由は何ですか?

テストしようとしている内容によって異なります。待ち時間に関心がある場合は時間/反復を使用し、スループットに関心がある場合は反復/時間を使用してください。


14

ベンチマークコードで計算された結果をどうにかして使用するようにしてください。さもなければあなたのコードは離れて最適化することができます。


12

2つのアルゴリズムを比較しようとしている場合は、それぞれについて少なくとも2つのベンチマークを行い、順番を入れ替えます。すなわち:

for(i=1..n)
  alg1();
for(i=1..n)
  alg2();
for(i=1..n)
  alg2();
for(i=1..n)
  alg1();

私は、異なるパスで同じアルゴリズムの実行時間にいくつかの顕著な違い(時々5-10%)を発見しました。

また、それを確かめなさいnは非常に大きいので、各ループの実行時間は最低でも10秒程度です。反復回数が多いほど、ベンチマーク時間の重要な数字が多くなり、データの信頼性が高まります。


  • 順序を自然に変更するとランタイムに影響します。 JVMの最適化とキャッシュ効果はここでうまくいくでしょう。より良いのは、ウォームアップをすることです。 JVM最適化、複数の実行の作成、および異なるJVMでのすべてのテストのベンチマーク。 - Mnementh

12

Javaでマイクロベンチマークを書くための落とし穴はたくさんあります。

まず、多少なりともランダムに時間がかかるあらゆる種類のイベントで計算する必要があります。ガベージコレクション、キャッシュ効果(ファイル用のOSおよびメモリ用のCPU)、IOなど。

第二:あなたは非常に短い間隔のための測定時間の正確さを信頼することはできません。

3番目:JVMは実行中にコードを最適化します。そのため、同じJVMインスタンス内での異なる実行はどんどん速くなります。

私の推奨事項:ベンチマークを数秒で実行するようにします。これは、ランタイムをミリ秒で実行するよりも信頼性が高くなります。 JVMをウォームアップします(JVMが最適化を実行できることを確認せずに少なくとも1回ベンチマークを実行することを意味します)。そしてベンチマークを複数回(たぶん5回)実行して中央値を取ります。すべてのマイクロベンチマークを新しいJVMインスタンスで実行します(すべてのベンチマークに新しいJavaを要求します)。そうしないと、JVMの最適化効果が後で実行されるテストに影響を与える可能性があります。実行しないでください。ウォームアップフェーズでは実行されません(これがクラスロードと再コンパイルを引き起こす可能性があるため)。


7

異なる実装を比較するときには、マイクロベンチマークの結果を分析することも重要である可能性があることにも注意してください。したがって、有意差検定作られるべきです。

実装だからですAベンチマークのほとんどの実行中は、実装よりも速いかもしれません。B。しかしAまた、より高いスプレッドを持つ可能性があるAと比較しても意味がありませんB

そのため、マイクロベンチマークを正しく作成して実行するだけでなく、正しく分析することも重要です。


6

http://opt.sourceforge.net/Java Micro Benchmark - さまざまなプラットフォーム上のコンピュータシステムの比較パフォーマンス特性を決定するために必要な制御タスク。最適化の決定を導き、異なるJava実装を比較するために使用できます。


  • 任意のJavaコードではなく、単にJVM +ハードウェアをベンチマークしているように見えます。 - Stefan L

5

他の優れたアドバイスに追加するには、私はまた次のことに留意する必要があります。

一部の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");

リンクされた質問


関連する質問

最近の質問