web-dev-qa-db-ja.com

System.nanoTime()はまったく役に立ちませんか?

ブログ投稿に記載されているようにJavaのSystem.nanoTime()に注意してください、x86システムでは、JavaのSystem.nanoTime( ) CPU 特定のカウンターを使用して時間値を返します。ここで、呼び出しの時間を測定するために使用する次のケースを考えます。

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

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

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

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

150
pdeva

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

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

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

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

他のプラットフォームについて語っているパートII(またはそれ以上)は見つかりませんが、その記事には、Linuxが同じ問題に遭遇し、同じ方法で同じ問題を解決したという発言が含まれています FAQ for clock_gettime(CLOCK_REALTIME) 、と言う:

  1. Clock_gettime(CLOCK_REALTIME)はすべてのプロセッサ/コアで一貫していますか? (Archは重要ですか?たとえば、ppc、arm、x86、AMD64、sparc)。

それはすべきであるか、バグだと考えられている。

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

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

したがって、nanoTimeclock_gettime(CLOCK_REALTIME)を呼び出すことを意味するものとしてホームズのリンクを読み取ることができる場合、x86のカーネル2.6.18以降、常にPowerPC上で安全です(IBMとMotorolaはIntelとは異なり、実際にマイクロプロセッサを設計する方法を知っています)。

残念ながら、SPARCやSolarisについては言及されていません。そしてもちろん、IBM JVMが何をするのかわかりません。しかし、最新のWindowsおよびLinux上のSun JVMはこれを正しく実現します。

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

203
Tom Anderson

ちょっと調べてみたところ、つまらないものであれば、それは役に立たないと考えられるかもしれません...特定の状況では...それはあなたの要求がどれだけ時間に敏感かによって異なります...

Java Sunサイトから this quote を確認してください。

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

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

Javaには nanoTime()の警告 メソッドもあります。

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

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

だからあなたの質問に答えるために... nanoTime()は役に立たないわけではありません...すべての状況で使用する最も賢明な方法ではありません.

35
mezoid

議論する必要はありません。ソースを使用してください。ここで、SE 6 for Linuxは、あなた自身の結論を下します:

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;
  }
}
18
blais

免責事項:私はこのライブラリの開発者です

あなたはこれがもっと好きかもしれません:

http://juliusdavies.ca/nanotime/

ただし、DLLまたはUnix .so(共有オブジェクト)ファイルを現在のユーザーのホームディレクトリにコピーして、JNIを呼び出せるようにします。

いくつかの背景情報は私のサイトにあります:

http://juliusdavies.ca/posix_clocks/clock_realtime_linux_faq.html

10
Julius Musseau

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

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

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

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

ただし、JDKがnanoTime()の安全性を保証しているにも関わらず、OpenJDKにはいくつかのバグがあり、特定のプラットフォームまたは特定の状況(たとえば JDK-8040140JDK-8184271 )。現在のところ、OpenJDK wrt nanoTime()に未解決の(既知の)バグはありませんが、OpenJDKの新しいリリースでのそのようなバグの発見やリグレッションは、誰にもショックを与えるべきではありません。

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

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

コード実行時間の測定に直接nanoTime()を使用する代わりに、たとえば JMH および async-profiler などの専用のベンチマークフレームワークとプロファイラーを使用することが望ましい 壁時計プロファイリングモード

9
leventov

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

6
Peter Lawrey

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

    long startNanos = System.nanoTime();

    Object returnValue = joinPoint.proceed();

    long elapsedNanos = System.nanoTime() - startNanos;

変数 'elapsedNanos'の値が負でした。 (中間呼び出しにも293年未満かかりましたが、これはlongに格納されたnanoのオーバーフローポイントです:)

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

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

5

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

フレームリフレッシュでグラフィックの位置を計算すると、nanoTime()がプログラムの動きを非常に滑らかにします。

そして、私はマルチコアマシンでのみテストします。

5
Yuvi Masory

いいえ、そうではありません...それはCPUに依存するだけです。CPUに応じて物事が異なる方法で処理される理由を 高精度イベントタイマー で確認してください。

基本的に、あなたのJavaのソースを読み、あなたのバージョンが関数で何をするかを確認し、それがCPUに対して動作する場合、それを実行します。

IBMも提案します パフォーマンスベンチマークに使用します(2008年の投稿ですが、更新されています)。

2
Ric Tokyo

これは、Windows XPおよびJRE 1.5.0_06を実行しているCore 2 Duoでは問題にならないようです。

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

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

2
starblue

ピーター・ローリーが良い答えを提供しているのと本質的に同じ議論にリンクしています。 System.nanoTime()を使用して負の経過時間を取得する理由

多くの人は、Java System.nanoTime()で負の時間を返すことができると述べました。他の人がすでに言ったことを繰り返すことを謝罪します。

  1. nanoTime()はクロックではなく、CPUサイクルカウンターです。
  2. 戻り値は、時間のように見えるように頻度で除算されます。
  3. CPU周波数が変動する場合があります。
  4. スレッドが別のCPUでスケジュールされると、nanoTime()を取得する可能性があり、その結果、マイナスの差が生じます。それは論理的です。 CPU間のカウンタは同期されません。
  5. 多くの場合、非常に誤解を招く結果が得られる可能性がありますが、デルタが負ではないためわかりません。考えてみてください。
  6. (未確認)命令を並べ替えると、同じCPUでも否定的な結果が得られる可能性があると思います。それを防ぐには、命令をシリアル化するメモリバリアを呼び出す必要があります。

System.nanoTime()が実行された場所でcoreIDを返した場合、それはクールです。

2
Vortex

Javaはクロスプラットフォームであり、nanoTimeはプラットフォームに依存しています。 Javaを使用する場合-nanoTimeを使用しない場合。この機能を使用して、さまざまなjvm実装で実際のバグを見つけました。

1
knave kilork

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

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

Java 5 APIドキュメント

0
Basu

また、System.currentTimeMillies()はシステムクロックを変更すると変更されますが、System.nanoTime()は変更されないため、継続時間を測定する方が安全です。

0
RobAu