web-dev-qa-db-ja.com

Javaのオーバーヘッドを測定する時間

低レベルで経過時間を測定する場合、次のいずれかを使用する選択肢があります。

System.currentTimeMillis();
System.nanoTime();

両方のメソッドはnativeで実装されています。 Cコードを掘り下げる前に、どちらか一方を呼び出す実質的なオーバーヘッドがあるかどうかを誰かが知っていますか?つまり、余分な精度をあまり気にしないのであれば、CPU時間の消費が少ないと予想されるのはどれですか?

N.B:標準のJava 1.6 JDKを使用していますが、この質問はどのJREにも有効です...

45
Lukas Eder

このページで正しいとマークされた答えは、実際には正しくありません。 JVMデッドコード除去(DCE)、オンスタック置換(OSR)、ループアンロールなどのため、これはベンチマークを記述するための有効な方法ではありません。OracleのJMHマイクロベンチマークフレームワークのようなフレームワークのみがそのようなものを適切に測定できます。このようなマイクロベンチマークの有効性について疑問がある場合は、 この投稿 をお読みください。

System.currentTimeMillis() vs System.nanoTime()のJMHベンチマークは次のとおりです。

_@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Benchmark)
public class NanoBench {
   @Benchmark
   public long currentTimeMillis() {
      return System.currentTimeMillis();
   }

   @Benchmark
   public long nanoTime() {
    return System.nanoTime();
   }
}
_

結果は次のとおりです(Intel Core i5で):

_Benchmark                            Mode  Samples      Mean   Mean err    Units
c.z.h.b.NanoBench.currentTimeMillis  avgt       16   122.976      1.748    ns/op
c.z.h.b.NanoBench.nanoTime           avgt       16   117.948      3.075    ns/op
_

これは、System.nanoTime()が呼び出しごとに〜123nsに比べて〜118nsでわずかに速いことを示しています。ただし、平均誤差を考慮に入れると、2つの間にほとんど違いがないことも明らかです。また、結果はオペレーティングシステムによって異なる可能性があります。しかし、一般的なポイントは、オーバーヘッドの点で本質的に同等であることです。

更新2015/08/25:この答えは、JMHを使用して測定する場合、そのほとんどを修正するのに近いものの、まだ正しくありません。 System.nanoTime()のようなもの自体を測定することは、特別な種類のねじれたベンチマークです。答えと決定的な記事は here です。

43
brettw

どちらのオーバーヘッドも心配する必要はないと思います。それは非常に小さいので、それ自体はほとんど測定できません。両方の簡単なベンチマークを次に示します。

_for (int j = 0; j < 5; j++) {
    long time = System.nanoTime();
    for (int i = 0; i < 1000000; i++) {
        long x = System.currentTimeMillis();
    }
    System.out.println((System.nanoTime() - time) + "ns per million");

    time = System.nanoTime();
    for (int i = 0; i < 1000000; i++) {
        long x = System.nanoTime();
    }
    System.out.println((System.nanoTime() - time) + "ns per million");

    System.out.println();
}
_

そして最後の結果:

_14297079ns per million
29206842ns per million
_

System.currentTimeMillis()System.nanoTime()の2倍の速さで表示されます。ただし、29nsは、とにかく測定する他のものよりもずっと短くなります。クロックに関連付けられていないので、精度と正確さのためにSystem.nanoTime()を選びます。

25
WhiteFang34

何かを実行するのにかかる時間を測定するためにのみSystem.nanoTime()を使用してください。ナノ秒の精度の問題ではありません。System.currentTimeMillis()は「ウォールクロックタイム」ですが、System.nanoTime()はタイミングをとることを目的としており、その他は。 System.nanoTime()のJavadocから:

この方法は経過時間を測定するためにのみ使用でき、システムまたは実時間のその他の概念とは関係ありません。

11
ColinD

時間があれば、 クリフクリックによるこの話 を見てください。彼はSystem.currentTimeMillisおよびその他のもの。

6
mindas

System.currentTimeMillis()は通常非常に高速です(5〜6 cpuサイクルに近いですが、これをどこで読んだかわかりません)が、プラットフォームによって解像度が異なります。

したがって、高精度が必要な場合はnanoTime()に進み、オーバーヘッドが心配な場合はcurrentTimeMillis()に進みます。

6

この質問に対する受け入れられた答えは確かに間違っています。 @brettwが提供する別の答えは良いですが、それでも詳細に光を当てています。

この件の完全な取り扱いとこれらの通話の実際の費用については、 https://shipilev.net/blog/2014/nanotrusting-nanotime/ をご覧ください。

質問に答えるには:

どちらかを呼び出す実質的なオーバーヘッドがあるかどうか誰もが知っていますか?

  • System#nanoTimeを呼び出すオーバーヘッドは、呼び出しごとに15〜30ナノ秒です。
  • nanoTimeによって報告される値、その解像度は、30ナノ秒に1回しか変更されません

これは、毎秒数百万のリクエストを実行しようとしているかどうかに応じて、nanoTimeを呼び出すことは、2番目の呼び出しnanoTimeの巨大なチャンクを事実上失うことを意味します。このようなユースケースでは、クライアント側からのリクエストを測定することを検討してください。したがって、 coordinated omission に陥らないように、キューの深さを測定することも良い指標です。

1秒間にできるだけ多くの作業を詰め込もうとしていない場合、nanoTimeは実際には重要ではありませんが、調整された省略は依然として要因です。

最後に、完全を期すために、currentTimeMillisはコストに関係なく使用できません。これは、2つの呼び出し間で前進することが保証されていないためです。特にNTPを備えたサーバーでは、currentTimeMillisは常に動き続けています。言うまでもなく、コンピューターで測定されるほとんどのものは1ミリ秒もかかりません。

4
pjulien

理論レベルでは、VMがネイティブスレッドを使用し、最新のプリエンプティブオペレーティングシステム上にある場合、currentTimeMillisはタイムスライスごとに1回だけ読み込まれるように実装できます。精度を犠牲にします。

2
Dilum Ranatunga