System.currentTimeMillisとSystem.nanoTime


379

精度対 精度

ゲームでのオブジェクトの位置を更新するときにSystem.currentTimeMillis()またはSystem.nanoTime()を使用する必要があるかどうかを知りたいですか?彼らの動きの変化は、最後の電話からの経過時間に正比例しているので、できるだけ正確にしたい。

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

私がリストした2つよりも優れたオプションはありますか?

提案/コメントはありますか?


81
nanoTime通常はcurrentTimeMillisよりもはるかに正確ですが、これも比較的コストのかかる呼び出しです。currentTimeMillis()少数(5〜6)のCPUクロックで実行されます。nanoTimeは基盤となるアーキテクチャに依存し、100以上のCPUクロックになる場合があります。
bestss

10
Windowsのタイムスライスの粒度が1000ms / 64であることはご存じでしょうか?これは15.625ミリ秒、つまり15625000ナノ秒です。

7
100クロックサイクルが追加されてもゲームに影響はないと思います。トレードオフの価値はおそらくあります。ゲームの更新ごとに1回だけメソッドを呼び出し、値をmemに保存する必要があるため、オーバーヘッドが大幅に増えることはありません。異なるプラットフォームの粒度については、私にはわかりません。
aglassman

9
Windowsのデフォルトのタイムスライス粒度は1000ms / 64です。これは、ネイティブのtimeBeginPeriod APIを使用して増やすことができます。最近のPCには、基本的なタイマーに加えて、高解像度のタイマーもあります。高解像度タイマーには、QueryPerformanceCounter呼び出しを介してアクセスできます。
Robin Davies

2
@Gohan-この記事では、pzemtsov.github.ioSystem.currentTimeMillis() / 2017/07/23
Attila

回答:


320

経過時間の非常に正確な測定値を探しているだけの場合は、を使用してくださいSystem.nanoTime()System.currentTimeMillis()エポックからのミリ秒単位で可能な最も正確な経過時間をSystem.nanoTime()提供しますが、任意のポイントに相対的なナノ秒精度の時間を提供します。

Javaドキュメントから:

public static long nanoTime()

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

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

たとえば、一部のコードの実行にかかる時間を測定するには:

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

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


20
精度と精度の違いを知っていますか?ナノ秒の精度で正確である方法はありません。
mmcdole 2008

6
申し訳ありません、私は正確を意味しました 私はその用語を大まかに使用していましたが、それが混乱を招く(そしてその単語の不適切な使用)ことに同意します。
dancavallaro 2008

4
@dancavallaro、情報ありがとうございます。よろしければ、ドキュメントからの引用を含むように回答を編集し、リンクを修正しました
mmcdole

135
この答えは、nanoTime()を選択する上で技術的には正しいですが、非常に重要な点を完全に覆い隠しています。ドキュメントが言うように、nanoTime()は高精度タイマーです。currentTimeMillis()はタイマーではなく、「壁掛け時計」です。nanoTime()は常に正の経過時間を生成しますが、currentTimeMillisは生成しません(たとえば、日付を変更した場合、うるう秒にヒットした場合など)。これは、システムのタイプによっては非常に重要な違いです。
charstar 2011

11
ユーザーは時間とNTP同期をcurrentTimeMillis変更しますが、DSTが原因で変更されるのはなぜですか DSTスイッチは、エポックを過ぎた秒数を変更しません。「掛け時計」かもしれませんが、UTCに基づいたものです。タイムゾーンとDST設定に基づいて、現地時間で何に変換するかを決定する必要があります(または、他のJavaユーティリティを使用してそれを行います)。
シャドウマン

100

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

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

System.currentTimeMillis() スレッド間で使用しても安全です。



3
さて、あなたは毎日何か新しいことを学びます。しかし、以前は安全ではなかった(確かにスレッド全体で意味のない読み取り値を返す)ことを考えると、そのような使用法はおそらく仕様の範囲外であるため、おそらく避ける必要があります。
gubby

4
@jgubby:非常に興味深い... 異なるスレッド間でSystem.nanoTime()呼び出しの結果を比較するのは安全はないサポートへの参照? 次のリンクを参照する価値があります:bugs.sun.com/bugdatabase/view_bug.do ?bug_id
6519418docs.oracle.com

15
docs.oracle.com/javase/7/docs/api/java/lang/…で説明されている説明を見ると、nanoTimeから返された値の違いは、それらが同じである限り、比較に有効であるように見えますJVM -The values returned by this method become meaningful only when the difference between two such values, obtained within the same instance of a Java virtual machine, is computed.
Tuxdude

7
のJavaDocはnanoTime()言う:同じ起源がJava仮想マシンのインスタンスでこのメソッドのすべての呼び出しによって使用されます。他の仮想マシンインスタンスは異なるオリジンを使用する可能性があります。つまり、スレッド全体で同じ結果返されます。
Simon Forsberg、2018

58

Arkadiyによる更新:System.currentTimeMillis()Windows 7のOracle Java 8での動作がより正確になりました。時刻は1ミリ秒の精度で返されました。OpenJDKのソースコードは変更されていないため、何がより良い動作を引き起こすのかわかりません。


SunのDavid Holmesが数年前に、JavaタイミングAPI(特にSystem.currentTimeMillis()System.nanoTime())を非常に詳細に調べたブログ記事を投稿しました。

ホットスポットVMの内部:クロック、タイマー、およびスケジューリングイベント-パートI-Windows

JavaでWindowsのJavaが時間待機パラメーターを持つAPIに使用するタイマーの非常に興味深い側面の1つは、他のAPI呼び出しが行われた可能性に応じてタイマーの解像度が変わる可能性があることです-システム全体(特定のプロセスだけでなく) 。彼は使用Thread.sleep()がこの解像度の変化を引き起こす例を示しています。


1
@アルカディ:更新のステートメントのソースはありますか?
Lii

@Lii-残念ながら、違います。この質問のコードを実行している私から来ています:stackoverflow.com/questions/34090914/…。このコードは、Java 7で15ミリ秒の精度、Java 8で1ミリ秒の精度を生成します

2
OpenJDKのhotspot / src / os / windows / vm / os_windows.cppのcurrentTimeMillisをos :: javaTimeMillisまで追跡しました(hg.openjdk.java.net/jdk8/jdk8/hotspot/file/87ee5ee27509/src/os/…) 。まだGetSystemTimeAsFileTimeであるように見えるため、変更がどこから発生するのかわかりません。またはそれが有効な場合でも。使用する前にテストしてください。

動作の変化GetSystemTimeAsFileTimeは、XPと7 での動作方法を変更した結果です 。詳細については、ここを参照してください(システム全体でいくつかのより正確なタイミングメソッドが導入されたため、tl; drはより正確になりました)。

11

System.nanoTime()古いJVMではサポートされていません。それが懸念される場合は、currentTimeMillis

精度については、ほぼ正しいです。一部のWindowsマシンでcurrentTimeMillis()は、解像度は約10ミリ秒(50ミリ秒ではない)です。理由はわかりませんが、一部のWindowsマシンはLinuxマシンと同じくらい正確です。

過去にGAGETimerを使用して、ある程度の成功を収めました。


3
「古いJVM」とはどのようなものですか?Java 1.2か何か?
Simon Forsberg

1
System.nanoTime()は2004年にJava 1.5で導入されました。2013年にJava 1.4のサポートが拡張されたため、System.nanoTime()が信頼できるようになり、この回答は古くなっています。
デイブL.

9

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

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

クロック時間を使用したいが、インターネット時間同期による不連続性を避けたい場合は、単にクロックを定期的にリセットするのではなく、クロックレートをゼロに「調整」するMeinbergなどのNTPクライアントを検討できます。

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


19
「currentTimeMillisは時刻であり、夏時間のために変化します ...このステートメントが偽であることを確認してください。 System.currentTimeMillis()1970年1月1日UTCの真夜中のUnix / Posixエポックからの経過時間(ミリ秒単位)を報告します。UTCは夏時間に合わせて調整されることはないため、ローカルタイムゾーンが夏時間のローカル時間にオフセットを追加したり、サブジェクトしたりしても、この値はオフセットされません。さらに、Java Time Scaleはうるう秒を平滑化するため、日数は引き続き86,400秒であるように見えます。
scottb

5

はい、そのような精度が必要な場合はを使用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)); }
    }

4

ゲームのグラフィックとスムーズな位置の更新には、ではSystem.nanoTime()なくを使用してくださいSystem.currentTimeMillis()。ゲームでcurrentTimeMillis()からnanoTime()に切り替えて、モーションの滑らかさを大幅に改善しました。

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

  • 壁時計の解像度以下の正確なピクセル配置
  • 必要に応じて、ピクセル間のアンチエイリアス機能
  • Windowsの時計の不正確さ
  • クロックジッター(壁時計が実際に前進するときの不整合)

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


1

私はナノタイムで良い経験をしました。JNIライブラリを使用して、壁時計の時間を2つの長い時間(エポックからの秒数とその秒内のナノ秒)として提供します。これは、WindowsとLinuxの両方でプリコンパイルされたJNIパーツで利用できます。


1

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

nanoTimeメソッドについて:

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

あなたが使用している場合はSystem.currentTimeMillis()( -将来へ戻る<)あなたの経過時間を否定することができ


-3

ここで1つのことは、nanoTimeメソッドの不整合です。同じ入力に対して非常に一貫した値を提供しません.currentTimeMillisは、パフォーマンスと整合性の点ではるかに優れており、nanoTimeほど正確ではありませんが、エラーのマージンが低くなります、したがって、その値の精度が高くなります。したがって、currentTimeMillisを使用することをお勧めします


6
他の回答やコメントに記載されているように、currentTimeMillisはシステムクロックの変更の影響を受けるため、JVMの以前のイベントからの経過時間を計算するにはあまり適していません。
デュエリンマーカー2013
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.