51

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

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

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

注意: 私は標準の Java 1.6 JDK を使用していますが、質問はどの JRE でも有効かもしれません...

4

8 に答える 8

45

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

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

@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 とわずかに高速であることを示しています。ただし、平均誤差を考慮に入れると、両者の間にほとんど差がないことも明らかです。また、結果はオペレーティング システムによって異なる可能性があります。しかし、一般的な要点は、オーバーヘッドに関しては本質的に同等であるということです。

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

于 2014-03-10T15:31:33.857 に答える
26

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

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()は の 2 倍の速度で表示されSystem.nanoTime()ます。ただし、29ns は、とにかく測定する他のものよりもはるかに短くなります。System.nanoTime()時計に関連していないので、精度と精度を求めます。

于 2011-04-12T19:28:20.457 に答える
11

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

このメソッドは、経過時間を測定するためにのみ使用でき、システム時間またはウォールクロック時間の他の概念とは関係ありません。

于 2011-04-12T19:29:23.017 に答える
7

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

したがって、高精度が必要な場合は をnanoTime()、オーバーヘッドが心配な場合は を選択してくださいcurrentTimeMillis()

于 2011-04-12T19:28:25.280 に答える
6

時間があれば、Cliff Click によるこのトークをご覧くださいSystem.currentTimeMillis

于 2011-04-12T21:47:44.417 に答える
4

この質問に対する受け入れられた答えは、実際には正しくありません。@brettwが提供する別の回答は良いですが、それでも詳細はわかりません。

このテーマの完全な扱いとこれらの呼び出しの実際のコストについては、https://shipilev.net/blog/2014/nanotrusting-nanotime/を参照してください。

尋ねられた質問に答えるには:

いずれかを呼び出す実質的なオーバーヘッドがあるかどうかは誰にもわかりませんか?

  • 呼び出しのオーバーヘッドは、呼び出しSystem#nanoTimeごとに 15 ~ 30 ナノ秒です。
  • によって報告される値nanoTime、その分解能は、30 ナノ秒ごとに 1 回だけ変化します

これは、1 秒あたり数百万のリクエストを実行しようとしているかどうかに応じて、呼び出しnanoTimeは 2 番目の呼び出しの膨大な量を効果的に失っていることを意味しますnanoTimeこのようなユースケースでは、クライアント側からのリクエストを測定することを検討してください。これにより、調整された省略に陥らないようにすることができます。キューの深さを測定することも良い指標です。

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

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

于 2016-11-20T15:37:15.283 に答える
2

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

于 2011-04-12T19:40:21.163 に答える