System.cienTimeMillis vs System.nanoTime


379

Độ chính xác Vs. Độ chính xác

Những gì tôi muốn biết là liệu tôi nên sử dụng System.civerseTimeMillis () hoặc System.nanoTime () khi cập nhật vị trí của đối tượng trong trò chơi của mình? Sự thay đổi chuyển động của họ tỷ lệ thuận với thời gian đã trôi qua kể từ cuộc gọi cuối cùng và tôi muốn chính xác nhất có thể.

Tôi đã đọc được rằng có một số vấn đề nghiêm trọng về độ phân giải thời gian giữa các hệ điều hành khác nhau (cụ thể là Mac / Linux có độ phân giải gần 1 ms trong khi Windows có độ phân giải 50ms ??). Tôi chủ yếu chạy các ứng dụng của mình trên windows và độ phân giải 50ms có vẻ không chính xác.

Có lựa chọn nào tốt hơn hai cái tôi liệt kê không?

Bất kỳ đề xuất / ý kiến?


81
nanoTimethường chính xác hơn chính xác so với currentTimeMillis nhưng đây cũng là một cuộc gọi tương đối đắt tiền. currentTimeMillis()chạy trong một vài (5-6) đồng hồ cpu, nanoTime phụ thuộc vào kiến ​​trúc cơ bản và có thể là hơn 100 đồng hồ cpu.
bestsss

10
Bạn có nhận ra rằng Windows thường có độ chi tiết 1000 lần / 64, phải không? Đó là 15,625ms, hoặc 15625000 Nam giây!

7
Tôi không nghĩ rằng một trăm chu kỳ đồng hồ thêm sẽ ảnh hưởng đến trò chơi của bạn và sự đánh đổi có lẽ sẽ đáng giá. Bạn chỉ nên gọi phương thức một lần cho mỗi lần cập nhật trò chơi sau đó lưu giá trị trong mem, vì vậy nó sẽ không thêm nhiều chi phí. Đối với độ chi tiết của các nền tảng khác nhau, tôi không có ý tưởng.
aglassman

9
Windows có độ chi tiết thời gian DEFAULT là 1000ms / 64. Bạn có thể tăng điều này thông qua API gốcBeginPeriod. Các PC hiện đại cũng có bộ hẹn giờ độ phân giải cao bên cạnh bộ hẹn giờ cơ bản. Bộ định thời độ phân giải cao có thể truy cập thông qua lệnh gọi QueryPerformanceCorer.
Robin Davies

2
@Gohan - Bài viết này đi sâu vào chi tiết về hoạt động bên trong của System.currentTimeMillis(): pzemtsov.github.io/2017/07/23/the-slow-cientimemillis.html
Attila Tanyi

Câu trả lời:


320

Nếu bạn chỉ tìm kiếm các phép đo cực kỳ chính xác về thời gian đã trôi qua , hãy sử dụng System.nanoTime(). System.currentTimeMillis()sẽ cung cấp cho bạn thời gian trôi qua chính xác nhất có thể tính bằng mili giây kể từ kỷ nguyên, nhưng System.nanoTime()cung cấp cho bạn thời gian chính xác đến nano giây, liên quan đến một số điểm tùy ý.

Từ Tài liệu Java:

public static long nanoTime()

Trả về giá trị hiện tại của bộ định thời hệ thống có sẵn chính xác nhất, tính bằng nano giây.

Phương pháp này chỉ có thể được sử dụng để đo thời gian trôi qua và không liên quan đến bất kỳ khái niệm nào khác về thời gian hệ thống hoặc đồng hồ treo tường. Giá trị được trả về đại diện cho nano giây do một số thời gian gốc cố định nhưng tùy ý (có thể trong tương lai, vì vậy giá trị có thể âm). Phương pháp này cung cấp độ chính xác nano giây, nhưng không nhất thiết là độ chính xác nano giây. Không có đảm bảo nào được thực hiện về tần suất thay đổi giá trị. Sự khác biệt trong các cuộc gọi liên tiếp kéo dài hơn khoảng 292 năm (2 63 nano giây) sẽ không tính toán chính xác thời gian đã trôi qua do tràn số.

Ví dụ: để đo thời gian thực hiện một số mã:

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

Xem thêm: JavaDoc System.nanoTime ()JavaDoc System.civerseTimeMillis () để biết thêm thông tin.


20
Bạn có chắc bạn biết sự khác biệt giữa độ chính xác và độ chính xác? Không có cách nào nó chính xác đến độ chính xác nano giây.
mmcdole

6
Xin lỗi, ý tôi là chính xác. Tôi đã sử dụng thuật ngữ một cách lỏng lẻo, nhưng tôi đồng ý rằng nó gây nhầm lẫn (và cách sử dụng từ này không đúng).
dancavallaro

4
@dancavallaro, cảm ơn thông tin. Nếu bạn không phiền, tôi đã chỉnh sửa câu trả lời của bạn để bao gồm một trích dẫn từ các tài liệu và sửa các liên kết
mmcdole

135
Câu trả lời này đúng về mặt kỹ thuật khi chọn nanoTime () nhưng hoàn toàn che đậy một điểm cực kỳ quan trọng. nanoTime (), như tài liệu nói, là một bộ đếm thời gian chính xác. currentTimeMillis () KHÔNG PHẢI LÀ TIMER, nó là "đồng hồ treo tường". nanoTime () sẽ luôn tạo ra thời gian trôi qua tích cực, currentTimeMillis sẽ không (ví dụ: nếu bạn thay đổi ngày, đạt một bước nhảy vọt, v.v.) Đây là một điểm khác biệt cực kỳ quan trọng đối với một số loại hệ thống.
charstar

11
Người dùng thay đổi thời gian và đồng bộ hóa NTP chắc chắn, nhưng tại sao sẽ currentTimeMillisthay đổi do DST? Công tắc DST không thay đổi số giây qua kỷ nguyên. Nó có thể là "đồng hồ treo tường" nhưng nó dựa trên UTC. Bạn phải xác định dựa trên các cài đặt múi giờ và DST của bạn, những gì chuyển thành giờ địa phương của bạn (hoặc sử dụng các tiện ích Java khác để làm điều đó cho bạn).
Shadow Man

100

Vì không ai khác nhắc đến điều này

Không an toàn để so sánh kết quả của các System.nanoTime()cuộc gọi giữa các luồng khác nhau. Ngay cả khi các sự kiện của các luồng xảy ra theo thứ tự có thể dự đoán được, sự khác biệt về nano giây có thể là dương hoặc âm.

System.currentTimeMillis() là an toàn để sử dụng giữa các chủ đề.


4
Trên Windows chỉ giữ đến SP2 theo: stackoverflow.com/questions/510462/iêu
Peter Schmitz

3
Vâng, bạn học được điều gì đó mới mỗi ngày. Tuy nhiên, tôi nghi ngờ rằng điều đó là không an toàn trong quá khứ (chắc chắn trả về các bài đọc vô nghĩa trên các chủ đề), việc sử dụng như vậy có lẽ vẫn nằm ngoài thông số kỹ thuật và vì vậy có lẽ nên tránh.
gubby

4
@jgubby: Rất thú vị ... có tham chiếu nào đến hỗ trợ không an toàn để so sánh kết quả của các cuộc gọi System.nanoTime () giữa các Chủ đề khác nhau không? Các liên kết sau có giá trị để xem: bugs.sun.com/bugdatabase/view_bug.do?bug_id=6519418 docs.oracle.com/javase/7/docs/api/java/lang/...
user454322

15
Nhìn vào mô tả được đề cập ở đây: docs.oracle.com/javase/7/docs/api/java/lang/ mẹo , có vẻ như sự khác biệt giữa các giá trị được trả về từ nanoTime là hợp lệ để so sánh miễn là chúng giống nhau 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 chonanoTime() biết: Cùng một nguồn gốc được sử dụng bởi tất cả các yêu cầu của phương thức này trong một thể hiện của máy ảo Java; các trường hợp máy ảo khác có thể sử dụng một nguồn gốc khác. có nghĩa là nó không quay trở lại giống nhau trên chủ đề.
Simon Forsberg

58

Cập nhật bởi Arkadiy : Tôi đã quan sát thấy hành vi đúng hơn của System.currentTimeMillis()Windows 7 trong Oracle Java 8. Thời gian được trả về với độ chính xác 1 mili giây. Mã nguồn trong OpenJDK không thay đổi, vì vậy tôi không biết điều gì gây ra hành vi tốt hơn.


David Holmes của Sun đã đăng một bài viết trên blog cách đây vài năm, có một cái nhìn rất chi tiết về các API thời gian Java (đặc biệt System.currentTimeMillis()System.nanoTime()), khi bạn muốn sử dụng chúng và cách chúng hoạt động nội bộ.

Bên trong Hotspot VM: Đồng hồ, Bộ hẹn giờ và Sự kiện Lập lịch - Phần I - Windows

Một khía cạnh rất thú vị của bộ đếm thời gian được Java sử dụng trên Windows cho các API có tham số chờ theo thời gian là độ phân giải của bộ hẹn giờ có thể thay đổi tùy thuộc vào những gì các lệnh gọi API khác có thể được thực hiện - toàn hệ thống (không chỉ trong quy trình cụ thể) . Ông cho thấy một ví dụ trong đó việc sử dụng Thread.sleep()sẽ gây ra thay đổi độ phân giải này.


1
@Arkadiy: Bạn có nguồn nào cho câu lệnh trong bản cập nhật không?
Lii

@Lii - Thật không may, không. Nó xuất phát từ việc tôi chạy mã trong câu hỏi này: stackoverflow.com/questions/34090914/ . Mã tạo ra độ chính xác 15ms với độ chính xác Java 7 và 1 ms với Java 8

2
Tôi lần theo currentTimeMillis để os :: javaTimeMillis trong hotspot / src / os / windows / vm / os_windows.cpp trong OpenJDK ( hg.openjdk.java.net/jdk8/jdk8/hotspot/file/87ee5ee27509/src/os/... ) . Có vẻ như vẫn là GetSystemTimeAsFileTime, vì vậy tôi không biết sự thay đổi đến từ đâu. Hoặc nếu nó thậm chí còn hợp lệ. Kiểm tra trước khi sử dụng.

sự thay đổi trong hành vi là kết quả của việc thay đổi cách GetSystemTimeAsFileTimelàm việc trong XP so với 7. Xem tại đây để biết thêm chi tiết (tl; dr nó chính xác hơn vì toàn bộ hệ thống đã giới thiệu một số phương pháp định thời chính xác hơn).

11

System.nanoTime()không được hỗ trợ trong các JVM cũ. Nếu đó là một mối quan tâm, hãy gắn bó vớicurrentTimeMillis

Về độ chính xác, bạn gần như chính xác. Trên MỘT SỐ máy Windows, currentTimeMillis()có độ phân giải khoảng 10ms (không phải 50ms). Tôi không chắc tại sao, nhưng một số máy Windows cũng chính xác như máy Linux.

Tôi đã sử dụng GAGETimer trong quá khứ với thành công vừa phải.


3
"JVMs cũ" như trong những JVM nào? Java 1.2 hay gì đó?
Simon Forsberg

1
System.nanoTime () đã được giới thiệu trong Java 1.5 vào năm 2004. Hỗ trợ mở rộng Java 1.4 vào năm 2013, do đó, khá an toàn khi nói System.nanoTime () hiện có thể được dựa vào và câu trả lời này đã hết hạn.
Dave L.

9

Như những người khác đã nói, currentTimeMillis là thời gian đồng hồ, thay đổi do thời gian tiết kiệm ánh sáng ban ngày, người dùng thay đổi cài đặt thời gian, giây nhuận và đồng bộ hóa thời gian internet. Nếu ứng dụng của bạn phụ thuộc vào các giá trị thời gian trôi qua đơn điệu, bạn có thể thích nanoTime hơn.

Bạn có thể nghĩ rằng người chơi sẽ không quan tâm đến cài đặt thời gian trong khi chơi trò chơi và có lẽ bạn đã đúng. Nhưng đừng đánh giá thấp sự gián đoạn do đồng bộ hóa thời gian trên internet hoặc có lẽ người dùng máy tính để bàn từ xa. API nanoTime miễn dịch với loại gián đoạn này.

Nếu bạn muốn sử dụng thời gian của đồng hồ, nhưng tránh sự gián đoạn do đồng bộ hóa thời gian trên internet, bạn có thể xem xét một ứng dụng khách NTP như Meinberg, "điều chỉnh" tốc độ đồng hồ về 0, thay vì chỉ đặt lại đồng hồ theo định kỳ.

Tôi nói từ kinh nghiệm cá nhân. Trong một ứng dụng thời tiết mà tôi đã phát triển, tôi đã nhận được những đợt tăng tốc gió ngẫu nhiên. Phải mất một thời gian tôi mới nhận ra rằng cơ sở thời gian của mình đã bị gián đoạn do hành vi của thời gian trên PC điển hình. Tất cả các vấn đề của tôi biến mất khi tôi bắt đầu sử dụng nanoTime. Tính nhất quán (tính đơn điệu) quan trọng đối với ứng dụng của tôi hơn độ chính xác thô hoặc độ chính xác tuyệt đối.


19
"currentTimeMillis là thời gian đồng hồ, thay đổi do thời gian tiết kiệm ánh sáng ban ngày" ... khá chắc chắn rằng tuyên bố này là sai. System.currentTimeMillis()báo cáo thời gian đã trôi qua (tính bằng mili giây) kể từ Epoch Unix / Posix là Nửa đêm, ngày 1 tháng 1 năm 1970 UTC. Vì UTC không bao giờ được điều chỉnh để tiết kiệm ánh sáng ban ngày, giá trị này sẽ không được bù khi múi giờ địa phương thêm hoặc giảm giá trị theo giờ địa phương để tiết kiệm ánh sáng ban ngày. Hơn nữa, Thang thời gian Java làm trôi đi những giây nhuận để ngày tiếp tục xuất hiện có 86.400 giây.
scottb

5

Có, nếu độ chính xác như vậy là bắt buộc sử dụng System.nanoTime(), nhưng lưu ý rằng sau đó bạn đang yêu cầu JVM Java 5+.

Trên các hệ thống XP của tôi, tôi thấy thời gian hệ thống được báo cáo ít nhất 100 micro giây là 278 nano giây sử dụng mã sau:

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

Đối với đồ họa trò chơi và cập nhật vị trí trơn tru, sử dụng System.nanoTime()chứ không phải System.currentTimeMillis(). Tôi đã chuyển từ currentTimeMillis () sang nanoTime () trong một trò chơi và có một sự cải thiện lớn về mặt hình ảnh về độ mượt của chuyển động.

Mặc dù một phần nghìn giây có vẻ như nó đã chính xác, nhưng về mặt trực quan thì không. Các yếu tố nanoTime()có thể cải thiện bao gồm:

  • định vị pixel chính xác dưới độ phân giải đồng hồ treo tường
  • khả năng chống bí danh giữa các pixel, nếu bạn muốn
  • Windows không chính xác đồng hồ treo tường
  • jitter đồng hồ (không nhất quán khi đồng hồ treo tường thực sự tích tắc)

Như các câu trả lời khác cho thấy, nanoTime có chi phí hiệu năng nếu được gọi liên tục - tốt nhất là gọi nó chỉ một lần trên mỗi khung và sử dụng cùng một giá trị để tính toán toàn bộ khung.


1

Tôi đã có kinh nghiệm tốt với nanotime . Nó cung cấp thời gian đồng hồ treo tường là hai giây (giây kể từ epoch và nano giây trong giây đó), sử dụng thư viện JNI. Nó có sẵn với phần JNI được biên dịch sẵn cho cả Windows và Linux.


1

System.currentTimeMillis()không an toàn cho thời gian trôi qua vì phương pháp này nhạy cảm với sự thay đổi đồng hồ thời gian thực của hệ thống. Bạn nên sử dụng System.nanoTime. Vui lòng tham khảo trợ giúp về Hệ thống Java:

Về phương pháp nanoTime:

.. Phương pháp này cung cấp độ chính xác nano giây, nhưng không nhất thiết là độ phân giải nano giây (nghĩa là tần suất thay đổi giá trị) - không có đảm bảo nào được thực hiện ngoại trừ độ phân giải ít nhất bằng độ phân giải của currentTimeMillis () ..

Nếu bạn sử dụng System.currentTimeMillis()thời gian trôi qua của mình có thể âm (Quay lại <- cho tương lai)


-3

một điều ở đây là sự không nhất quán của phương thức nanoTime. Nó không cung cấp các giá trị rất nhất quán cho cùng một đầu vào. , và do đó chính xác hơn trong giá trị của nó. do đó tôi sẽ đề nghị bạn sử dụng currentTimeMillis


6
Như đã lưu ý trong các câu trả lời và nhận xét khác, currentTimeMillis có thể thay đổi đồng hồ hệ thống và do đó, một lựa chọn kém để tính thời gian đã trôi qua kể từ một số sự kiện trước đó trong JVM.
đánh dấu duelin
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.