160

ブログ投稿Beware of System.nanoTime() in Javaに記載されているように、x86 システムでは、Java の System.nanoTime() はCPU固有のカウンターを使用して時間値を返します。ここで、通話時間を測定するために使用する次のケースを考えてみましょう。

long time1= System.nanoTime();
foo();
long time2 = System.nanoTime();
long timeSpent = time2-time1;

現在、マルチコア システムでは、time1 を測定した後、スレッドは、カウンターが前の CPU のカウンターよりも小さい別のプロセッサにスケジュールされている可能性があります。したがって、time1 よりも小さいtime2 の値を取得できます。したがって、timeSpent で負の値が得られます。

この場合を考えると、System.nanotime は今のところほとんど役に立たないのではないでしょうか。

システム時刻を変更しても nanotime に影響しないことはわかっています。それは私が上で説明した問題ではありません。問題は、電源がオンになってから各 CPU が異なるカウンターを保持することです。このカウンタは、最初の CPU と比較して 2 番目の CPU で低くなる可能性があります。スレッドは、OS によって time1 の取得後に 2 番目の CPU にスケジュールされる可能性があるため、timeSpent の値は正しくなく、マイナスになることさえあります。

4

14 に答える 14

212

この回答は、当時のオペレーティング システムで実行されていた当時の Sun JDK が実際に行ったことの観点から、2011 年に書かれました。それはずっと前だった!leventov's answerは、より最新の視点を提供します。

その投稿は間違っており、nanoTime安全です。この投稿には、Sun のリアルタイムおよび同時実行の担当者であるDavid Holmes によるブログ投稿へのリンクがあるコメントがあります。それは言います:

System.nanoTime() は、QueryPerformanceCounter/QueryPerformanceFrequency API を使用して実装されます [...] QPC によって使用されるデフォルトのメカニズムは、ハードウェア アブストラクション レイヤー (HAL) によって決定されます [...] このデフォルトは、ハードウェア全体だけでなく、OS 全体でも変更されますバージョン。たとえば、Windows XP Service Pack 2 では、プロセッサ タイムスタンプ カウンター (TSC) ではなく電源管理タイマー (PMTimer) を使用するように変更されました。これは、TSC が SMP システムの異なるプロセッサで同期されないという問題と、その頻度によるものです。電源管理設定に基づいて変化する可能性があります (したがって、経過時間との関係)。

したがって、Windows では、これWinXP SP2 までは問題でしたが、現在はありません。

他のプラットフォームについて説明しているパート II (またはそれ以上) は見つかりませんが、その記事には、Linux が同じ問題に遭遇し、同じ方法で解決したというコメントが含まれており、clock_gettime(CLOCK_REALTIME) の FAQへのリンクが含まれています。 、それは言う:

  1. すべてのプロセッサ/コアで clock_gettime(CLOCK_REALTIME) は一貫していますか? (arch は重要ですか? 例: ppc、arm、x86、amd64、sparc)。

バグがあると見なされるか、バグがあると見なされます。

ただし、x86/x86_64 では、同期されていない、または可変周波数の TSC が時間の不一致を引き起こす可能性があります。2.4 カーネルはこれに対する保護がまったくなく、初期の 2.6 カーネルもここではうまく機能しませんでした。2.6.18 以降では、これを検出するロジックが改善されており、通常は安全なクロックソースにフォールバックします。

ppc には常に同期されたタイムベースがあるため、問題になることはありません。

したがって、Holmes のリンクが をnanoTime呼び出すことを暗示していると解釈できる場合clock_gettime(CLOCK_REALTIME)、x86 上のカーネル 2.6.18 の時点で、常に PowerPC 上で安全に動作します (Intel とは異なり、IBM と Motorola は実際にマイクロプロセッサの設計方法を知っているため)。

悲しいことに、SPARC や Solaris についての言及はありません。そしてもちろん、IBM JVM が何をするのかはわかりません。しかし、最新の Windows および Linux 上の Sun JVM では、これが正しく行われます。

編集:この回答は、引用元に基づいています。しかし、それが実際には完全に間違っているのではないかと心配しています。いくつかの最新情報は本当に価値があります。Linux の時計に関する 4 年前の新しい記事へのリンクを見つけました。これは役に立つかもしれません。

于 2011-01-03T21:36:46.703 に答える
35

少し調べてみたところ、衒学的な人は役に立たないと考えられることがわかりました...特定の状況では...要件がどれほど時間に敏感かによって異なります...

Java Sun サイトからのこの引用を確認してください。

リアルタイム クロックと System.nanoTime() はどちらも同じシステム コールに基づいているため、同じクロックを使用します。

Java RTS では、すべての時間ベースの API (たとえば、タイマー、定期スレッド、デッドライン監視など) は高解像度タイマーに基づいています。また、リアルタイムの優先度とともに、適切なコードがリアルタイムの制約に対して適切なタイミングで実行されるようにすることができます。対照的に、通常の Java SE API は、高解像度の時間を処理できるメソッドをいくつか提供するだけで、特定の時間に実行される保証はありません。コード内のさまざまなポイント間で System.nanoTime() を使用して経過時間の測定を実行すると、常に正確になります。

Java には、nanoTime()メソッドに関する注意事項もあります。

このメソッドは、経過時間を測定するためにのみ使用でき、システム時間またはウォールクロック時間の他の概念とは関係ありません。返される値は、固定された任意の時間からのナノ秒を表します (おそらく将来であるため、値は負の可能性があります)。この方法はナノ秒の精度を提供しますが、必ずしもナノ秒の精度ではありません。値が変更される頻度については保証されません。約 292.3 年 (2 63 ナノ秒) を超える連続する呼び出しの違いは、数値のオーバーフローにより、経過時間を正確に計算できません。

引き出すことができる唯一の結論は、nanoTime() が正確な値として信頼できないということです。そのため、わずかナノ秒間隔の時間を測定する必要がない場合は、結果の戻り値が負であっても、この方法で十分です。ただし、より高い精度が必要な場合は、JAVA RTS の使用を推奨しているようです。

したがって、あなたの質問に答えるために... no nanoTime() は役に立たないわけではありません....あらゆる状況で使用する最も賢明な方法ではありません。

于 2009-02-04T11:21:17.303 に答える
27

Java 7 以降System.nanoTime()、JDK 仕様により安全性が保証されています。 System.nanoTime()の Javadocは、JVM 内で (つまり、すべてのスレッドにわたって) 観測されたすべての呼び出しが単調であることを明らかにしています。

返される値は、一定ではあるが任意の起点時刻からのナノ秒を表します (おそらく将来であるため、値は負の可能性があります)。Java 仮想マシンのインスタンスでは、このメソッドのすべての呼び出しで同じオリジンが使用されます。他の仮想マシン インスタンスは別のオリジンを使用する可能性があります。

JVM/JDK 実装は、基礎となる OS ユーティリティが呼び出されたときに観察される可能性のある不整合を解決する責任があります (たとえば、Tom Anderson の回答で言及されているもの)。

この質問に対する他の古い回答 (2009 年から 2012 年に書かれたもの) の大部分は、おそらく Java 5 または Java 6 に関連していたが、Java の最新バージョンには関連していない FUD を表現しています。

ただし、JDKnanoTime()の安全性は保証されていますが、OpenJDK にはいくつかのバグがあり、特定のプラットフォームや特定の状況 ( JDK-8040140JDK-8184271 など) ではこの保証が守られていないことに注意してください。現時点では、OpenJDK に関して未解決の (既知の) バグはありませnanoTime()んが、OpenJDK の新しいリリースでそのようなバグやリグレッションが発見されても、誰も驚かないはずです。

そのことを念頭に置いて、時間指定ブロッキング、インターバル待機、タイムアウトなどを使用するコードは、例外をスローするのではなく、負の時間差 (タイムアウト) をゼロとして処理することが望ましいです。nanoTime()、、java.util.concurrent.*などの のすべてのクラスのすべての時限待機メソッドの動作と一致するため、この方法も推奨されます。Semaphore.tryAcquire()Lock.tryLock()BlockingQueue.poll()

それにもかかわらず、nanoTime()時間ブロック、間隔待機、タイムアウトなどを実装するcurrentTimeMillis()ためには、後者が「時間の逆行」現象(たとえば、サーバー時間の修正による)の影響を受けるため、依然として優先される必要があります。つまり、currentTimeMillis()時間間隔の測定には適していません。まったく。詳細については、この回答を参照してください。

コード実行時間の測定に直接使用する代わりにnanoTime()、専用のベンチマーク フレームワークとプロファイラーを使用することをお勧めします。たとえば、ウォール クロック プロファイリング モードのJMHasync-profilerなどです。

于 2019-02-07T05:35:40.617 に答える
18

議論する必要はありません。ソースを使用してください。ここで、Linux 用の SE 6 について、独自の結論を出してください。

jlong os::javaTimeMillis() {
  timeval time;
  int status = gettimeofday(&time, NULL);
  assert(status != -1, "linux error");
  return jlong(time.tv_sec) * 1000  +  jlong(time.tv_usec / 1000);
}


jlong os::javaTimeNanos() {
  if (Linux::supports_monotonic_clock()) {
    struct timespec tp;
    int status = Linux::clock_gettime(CLOCK_MONOTONIC, &tp);
    assert(status == 0, "gettime error");
    jlong result = jlong(tp.tv_sec) * (1000 * 1000 * 1000) + jlong(tp.tv_nsec);
    return result;
  } else {
    timeval time;
    int status = gettimeofday(&time, NULL);
    assert(status != -1, "linux error");
    jlong usecs = jlong(time.tv_sec) * (1000 * 1000) + jlong(time.tv_usec);
    return 1000 * usecs;
  }
}
于 2011-04-01T17:07:25.093 に答える
7

Linux は CPU 間の不一致を修正しますが、Windows は修正しません。System.nanoTime() の精度は約 1 マイクロ秒であると想定することをお勧めします。時間を長くする簡単な方法は、foo() を 1000 回以上呼び出し、時間を 1000 で割ることです。

于 2009-02-05T23:17:48.290 に答える
5

System.nanoTime() を使用すると、負の経過時間が報告されるのを見てきました。明確にするために、問題のコードは次のとおりです。

    long startNanos = System.nanoTime();

    Object returnValue = joinPoint.proceed();

    long elapsedNanos = System.nanoTime() - startNanos;

変数「elapsedNanos」には負の値がありました。(私は、中間呼び出しも 293 年未満かかったと確信しています。これは、ロングに格納されたナノのオーバーフロー ポイントです:)

これは、AIX を実行する IBM P690 (マルチコア) ハードウェアで IBM v1.5 JRE 64 ビットを使用して発生しました。このエラーは 1 回しか発生していないので、非常にまれです。原因はわかりません。ハードウェア固有の問題なのか、JVM の欠陥なのか、わかりません。また、一般的な nanoTime() の精度への影響もわかりません。

元の質問に答えるために、nanoTimeが役に立たないとは思いません.1ミリ秒未満のタイミングを提供しますが、実際には(理論的なだけでなく)不正確になるリスクがあり、考慮する必要があります。

于 2011-01-03T20:23:10.717 に答える
5

絶対に駄目です。タイミング愛好家はマルチコアの問題を正しく指摘していますが、実際のアプリケーションでは currentTimeMillis() よりも大幅に優れていることがよくあります。

フレーム リフレッシュでグラフィックス位置を計算する場合、nanoTime() を使用すると、プログラムの動きが非常にスムーズになります。

また、マルチコア マシンでのみテストします。

于 2010-03-31T13:27:09.373 に答える
2

これは、WindowsXPおよびJRE1.5.0_06を実行しているCore2Duoでは問題にならないようです。

3つのスレッドを使用したテストでは、System.nanoTime()が逆方向に進むことはありません。プロセッサは両方ともビジーであり、スレッドは時々スリープ状態になり、スレッドの移動を引き起こします。

[編集]物理的に離れたプロセッサでのみ発生すると思います。つまり、同じダイ上の複数のコアに対してカウンタが同期されます。

于 2009-02-04T10:05:59.263 に答える
2

いいえ、そうではありません... CPUによって異なります。高精度イベントタイマーをチェックして、CPUによって処理が異なる方法/理由を確認してください。

基本的に、Javaのソースを読み取り、バージョンが関数で何をするかを確認し、それがCPUに対して機能する場合は、それを実行します。

IBMは、パフォーマンスのベンチマークに使用することをお勧めします(2008年の投稿ですが、更新されています)。

于 2009-02-04T08:13:25.763 に答える
0

Java 5 のドキュメントでも、同じ目的でこのメソッドを使用することを推奨しています。

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

Java 5 API ドキュメント

于 2009-02-04T08:31:06.053 に答える
-3

nanoTimeタイミングが非常に不安定です。基本的な素数性テスト アルゴリズムで試してみたところ、同じ入力に対して文字通り 1 秒離れた答えが得られました。そのばかげた方法を使用しないでください。get time ミリ秒よりも正確で正確なものが必要ですが、 ほど悪くはありませんnanoTime

于 2012-04-24T04:46:18.713 に答える