web-dev-qa-db-ja.com

System.currentTimeMillisとSystem.nanoTime

精度とV。精度

私が知りたいのは、自分のオブジェクトを更新するときにSystem.currentTimeMillis()またはSystem.nanoTime()のどちらを使用するかです。私のゲームでのポジションは?彼らの動きの変化は、前回の電話からの経過時間に正比例し、できるだけ正確になりたいのです。

私は、異なるオペレーティングシステム間で深刻な時間分解能の問題がいくつかあることを読みました(すなわち、Mac/Linuxの分解能はほぼ1ms、Windowsの分解能は50msです)。私は主にWindows上で自分のアプリを実行していますが、50msの解像度はかなり不正確なようです。

私がリストした2つよりも良い選択肢はありますか?

何か提案/コメント?

352
mmcdole

経過時間の非常に正確な測定値を探しているだけの場合は、System.nanoTime()を使用してください。 System.currentTimeMillis()は、エポック以降のミリ秒単位の最も正確な経過時間を示しますが、System.nanoTime()は、任意の時点を基準にした、ナノ秒単位の正確な時間を示します。

Javaの資料から

public static long nanoTime()

最も正確に利用可能なシステムタイマーの現在の値をナノ秒単位で返します。

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

たとえば、コードの実行にかかる時間を測定するには、次のようにします。

long startTime = System.nanoTime();    
// ... the code being measured ...    
long estimatedTime = System.nanoTime() - startTime;

詳細については、 JavaDoc System.nanoTime() および JavaDoc System.currentTimeMillis() も参照してください。

304
dancavallaro

誰もこれについて言及していないので…

異なるスレッド間でSystem.nanoTime()呼び出しの結果を比較するのは安全ではありません。スレッドのイベントが予測可能な順序で発生したとしても、ナノ秒の差はプラスにもマイナスにもなり得ます。

System.currentTimeMillis()はスレッド間で安全に使用できます。

90
gubby

Arkadiy による更新:Oracle 7のWindows 7上でのSystem.currentTimeMillis()のより正しい動作を観察しました。時間は1ミリ秒の精度で返されました。 OpenJDKのソースコードは変更されていないので、動作が良くなる原因がわかりません。


SunのDavid Holmesが数年前にブログ記事を投稿しました。この記事では、JavaタイミングAPI(特にSystem.currentTimeMillis()System.nanoTime())について、どれを使いたいのか、またそれらがどのように内部で機能するのかについて詳しく説明しています。

ホットスポットVMの内部:時計、タイマー、およびスケジュールイベント - パートI - ウィンドウ

時間指定された待機パラメーターを持つAPIのためにWindows上のJavaで使用されるタイマーの非常に興味深い側面の1つは、他のAPI呼び出しが行われたことによってタイマーの解像度が変わる可能性があるということです。 。彼はThread.sleep()を使うことでこの解像度が変わる例を示しています。

58
Michael Burr

System.nanoTime()は、古いJVMではサポートされていません。それが心配な場合は、currentTimeMillisを守ってください。

正確さに関しては、あなたはほとんど正しいです。一部のWindowsマシンでは、currentTimeMillis()の分解能は約10msです(50msではありません)。理由はわかりませんが、Windowsマシンの中にはLinuxマシンと同じくらい正確なものもあります。

私は過去に GAGETimer を使って、中程度の成功を収めています。

10
Paul Morel

他の人が言っているように、currentTimeMillisは時計の時間であり、それは夏時間、時間設定の変更、うるう秒、そしてインターネットの時刻同期によって変化します。あなたのアプリが単調に増加する経過時間の値に依存している場合は、代わりにnanoTimeをお勧めします。

あなたは、プレイヤーがゲームプレイ中に時間設定をいじるのではないと思うかもしれません、そして多分あなたは正しいでしょう。しかし、インターネットの時刻同期や、おそらくリモートデスクトップユーザーによる混乱を過小評価しないでください。 nanoTime APIはこのような混乱の影響を受けません。

クロックタイムを使用したいが、インターネットの時刻同期による不連続性を避けたい場合は、MeinbergのようにNTPクライアントを検討してください。定期的にクロックします。

私は個人的な経験から話します。私が開発した気象アプリケーションでは、ランダムに発生する風速スパイクが発生していました。私のタイムベースが典型的なPCのクロックタイムの振る舞いによって混乱していることに気付くのにしばらく時間がかかりました。私がnanoTimeを使い始めたとき、私の問題はすべて消えました。一貫性(単調性)は、生の精度や絶対精度よりも、私のアプリケーションにとってより重要でした。

8
KarlU

はい、そのような精度が必要な場合はSystem.nanoTime()を使用しますが、その場合はJava 5+ JVMが必要になることに注意してください。

私のXPシステムでは、少なくともシステム時間が報告されているのがわかります。 100マイクロ秒 次のコードを使用して、278ナノ秒

private void test() {
    System.out.println("currentTimeMillis: "+System.currentTimeMillis());
    System.out.println("nanoTime         : "+System.nanoTime());
    System.out.println();

    testNano(false);                                                            // to sync with currentTimeMillis() timer tick
    for(int xa=0; xa<10; xa++) {
        testNano(true);
        }
    }

private void testNano(boolean shw) {
    long strMS=System.currentTimeMillis();
    long strNS=System.nanoTime();
    long curMS;
    while((curMS=System.currentTimeMillis()) == strMS) {
        if(shw) { System.out.println("Nano: "+(System.nanoTime()-strNS)); }
        }
    if(shw) { System.out.println("Nano: "+(System.nanoTime()-strNS)+", Milli: "+(curMS-strMS)); }
    }
5
Lawrence Dol

ゲームグラフィックとスムーズな位置更新のために、System.nanoTime()ではなくSystem.currentTimeMillis()を使います。ゲーム内でcurrentTimeMillis()からnanoTime()に切り替えたところ、動きの滑らかさが視覚的に大幅に改善されました。

1ミリ秒はすでに正確であるように見えるかもしれませんが、視覚的にはそうではありません。 nanoTime()が改善できる要因には以下のものがあります。

  • 壁時計解像度以下の正確なピクセル配置
  • 必要に応じて、ピクセル間のアンチエイリアス機能
  • Windowsの壁時計の不正確さ
  • クロックジッタ(実際には、クロックが実際に進むタイミングの矛盾)

他の回答が示唆するように、nanoTimeは繰り返し呼び出されるとパフォーマンスが低下します。フレームごとに1回呼び出して同じ値を使用してフレーム全体を計算するのが最善です。

2
Thomas W

私は nanime を使った経験があります。 JNIライブラリを使用して、実時間を2倍(エポックからの秒数とその秒以内のナノ秒数)で提供します。それはWindowsとLinuxの両方のためにプリコンパイルされたJNI部分で利用可能です。

2
Jon Bright

System.currentTimeMillis()は経過時間に対して安全ではありません。この方法はシステムのシステムのリアルタイムクロックの変更に敏感なためです。あなたはSystem.nanoTimeを使うべきです。 Javaシステムのヘルプを参照してください。

NanoTime法について

..このメソッドはナノ秒の精度を提供しますが、必ずしもナノ秒の解像度(つまり、値が変更される頻度)はありません。解像度が少なくともcurrentTimeMillis()の精度と同じであること以外は保証されません。

System.currentTimeMillis()を使うと、経過時間はマイナスになることがあります(Back < - to the future)

0
Ricardo Gasca