4

この質問では、Java コードのパフォーマンスをテストする方法の伝承について質問したいと思います。通常のアプローチは次のように機能します。

long start = System.nanoTime();

for( int i=0; i<SOME_VERY_LARGE_NUMBER; i++) {
    ...do something...
}

long duration = System.nanoTime() - start;
System.out.println( "Performance: " 
    + new BigDecimal( duration ).divide( 
      new BigDecimal( SOME_VERY_LARGE_NUMBER, 3, RoundingMode.HALF_UP ) ) );

「最適化された」バージョンでは、呼び出しがループに移動し、比較よりもはるかに長い時間がかかるため (実行時の動作で予測しにくいため) System.nanoTime()、エラー マージンが大きくなります。System.nanoTime()i ++

私の批判は次のとおりです。

これにより平均ランタイムが得られますが、この値はあまり興味のない要因によって異なります。たとえば、テスト ループの実行中のシステム負荷や、JIT/GC が開始されたときのジャンプなどです。

ほとんどの場合、このアプローチの方が (はるかに) 優れているのではないでしょうか?

  1. JIT コンパイルを強制するのに十分な頻度でコードを実行して測定する
  2. コードをループで実行し、実行時間を測定します。最小値を記憶し、この値が安定したらループを中止します。

私の理論的根拠は、通常、一部のコードの速度 (下限) を知りたいということです。外部イベント (マウスの動き、デスクトップにアナログ時計があるためグラフィックス カードからの割り込み、スワッピング、ネットワーク パケットなど) によって任意のコードが遅くなる可能性がありますが、ほとんどの場合、どれだけ速いか知りたいだけです。私のコードは完璧な状況下にある可能性があります。

また、コードを数秒または数分実行する必要がないため (不要な影響をキャンセルするため)、パフォーマンス測定がはるかに高速になります。

誰かがこれを確認/デバンクできますか?

4

3 に答える 3

3

あなたが提案していることはかなり合理的だと思いますが、いくつかの調整があります。

1) 最小値ではなく、中央値 (またはパーセンタイルの束) を報告します。コードがガベージ コレクターに多くの負担をかける場合、単純に最小値を取るだけでは、簡単にそれを拾うことができません (必要なのは、2 つの連続する GC 一時停止の間に 1 回の反復が収まるだけです)。

2) 多くの場合、実時間ではなく CPU 時間を測定する方が理にかなっています。これにより、同じボックスで他のコードを実行することによる影響の一部が処理されます。

3) 一部のベンチマーク ツールでは、2 つのレベルのループが使用されます。内側のループは操作を繰り返し実行し、外側のループは内側のループの前後のクロックを調べます。次に、外側のループの反復全体で観測値が集計されます。

最後に、注意すべき JVM 固有の問題の非常に優れた概要を以下に示します。Java で正しいマイクロベンチマークを作成するにはどうすればよいですか?

于 2012-05-11T11:54:14.297 に答える
2

-XX:CompileThreshold JVM オプションを使用して、JIT が開始されるタイミングを指定できます。次に、タイミング ループを実行する前に、CompileThreshold より大きいループを実行して、テストを「ウォームアップ」できます。

于 2012-05-11T11:55:39.040 に答える
1

SOME_VERY_LARGE_NUMBER ループを 50 回実行し、最もパフォーマンスの高いループの平均を計算します。これは、コードのマイクロ ベンチマークだけでなく、他のベンチマークでも通常行われていることです。

また、GC に起因するパフォーマンスの問題は、多くの場合、コードの一部であると主張したいと思います。多くのメモリを割り当てるルーチンは、そのためにベンチマークに関して一定の価格を支払わなければならないため、おそらく GC を方程式から外すべきではありません。SOME_VERY_LARGE_NUMBER を十分に大きく選択した場合、提案されたアプローチでは、呼び出しごとの平均 GC コストが考慮されます。

あなたの提案について: すべてのタイマーには精度が限られているため、短いルーチンが 0 クロック ティック以内に完了する可能性があります。これは、ルーチンがゼロ時間で実行されることをアルゴリズムが検出することを意味します。これは明らかに正しくありません。

于 2012-05-11T11:39:49.813 に答える