Solaris SPARC ボックスでいくつかの Java コードのベンチマークを行っているときに、ベンチマークされた関数を初めて呼び出したときに、実行速度が非常に遅い (10 倍の差) ことに気付きました。
- 最初 | 1 | 25295.979 ミリ秒
- セカンド | 1 | 2256.990 ミリ秒
- サード | 1 | 2250.575 ミリ秒
どうしてこれなの?JIT コンパイラが疑わしいのですが、これを確認する方法はありますか?
編集:いくつかの回答に照らして、このコードがこの動作を示すことができる最も単純なテストケースであることを明確にしたかったのです。したがって、私の目標は、高速に実行することではなく、何が起こっているのかを理解して、実際のベンチマークで回避できるようにすることです。
解決済み: Tom Hawtin は、私の "SLOW" タイムが実際には妥当であると正しく指摘しました。この観察に続いて、デバッガーを Java プロセスに接続しました。最初のループでは、内側のループは次のようになります。
0xf9037218: cmp %l0, 100
0xf903721c: bge,pn %icc,0xf90371f4 ! 0xf90371f4
0xf9037220: nop
0xf9037224: ld [%l3 + 92], %l2
0xf9037228: ld [%l2 + 8], %l6
0xf903722c: add %l6, 1, %l5
0xf9037230: st %l5, [%l2 + 8]
0xf9037234: inc %l0
0xf9037238: ld [%l1], %g0
0xf903723c: ba,pt %icc,0xf9037218 ! 0xf9037218
次の反復では、ループは次のようになります。
0xf90377d4: sub %l2, %l0, %l3
0xf90377d8: add %l3, %l0, %l2
0xf90377dc: add %l2, 1, %l4
0xf90377e0: inc %l0
0xf90377e4: cmp %l0, 100
0xf90377e8: bl,pn %icc,0xf90377d8 ! 0xf90377d8
そのため、HotSpot は内側のループからメモリ アクセスを削除し、1 桁高速化しました。
レッスン:計算してみよう! トムの計算は自分でやるべきだった.
ベンチマーク Java コード:
private int counter;
private int nThreads;
private void measure(String tag) throws Exception {
MyThread threads[] = new MyThread[nThreads];
int i;
counter = 0;
for (i = 0; i < nThreads; i++)
threads[i] = new MyThread();
long start = System.nanoTime();
for (i = 0; i < nThreads; i++)
threads[i].start();
for (i = 0; i < nThreads; i++)
threads[i].join();
if (tag != null)
System.out.format("%-20s | %-2d | %.3f ms \n", tag, nThreads,
new Double((System.nanoTime() - start) / 1000000.0));
}
public MyBench() {
try {
this.nThreads = 1;
measure("First");
measure("Second");
measure("Third");
} catch (Exception e) {
System.out.println("Error: " + e);
}
}
private class MyThread extends Thread {
public void run() {
while (counter < 10000000) {
// work
for (int j = 0; j < 100; j++)
counter++;
counter -= 99;
}
}
}