[java] System.currentTimeMillis 및 System.nanoTime

정확도 대. 정도

게임에서 객체의 위치를 ​​업데이트 할 때 System.currentTimeMillis () 또는 System.nanoTime ()을 사용해야하는지 여부를 알고 싶습니다 . 그들의 움직임의 변화는 마지막 호출 이후 경과 시간에 직접 비례하며 가능한 한 정확하고 싶습니다.

다른 운영 체제 사이에 심각한 시간 해결 문제가 있음을 읽었습니다 (즉, Mac / Linux의 해상도는 거의 1ms이고 Windows의 해상도는 50ms입니다 ??). 나는 주로 Windows에서 내 앱을 실행하고 있으며 50ms 해상도는 매우 정확하지 않은 것 같습니다.

내가 나열된 두 가지보다 더 나은 옵션이 있습니까?

제안이나 의견이 있으십니까?



답변

경과 시간 을 매우 정밀하게 측정 하려면을 사용하십시오 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 () 를 참조하십시오.


답변

아무도 이것을 언급하지 않았기 때문에…

System.nanoTime()다른 스레드 간 호출 결과를 비교하는 것은 안전하지 않습니다 . 스레드의 이벤트가 예측 가능한 순서로 발생하더라도 나노초의 차이는 양수 또는 음수 일 수 있습니다.

System.currentTimeMillis() 스레드간에 사용하기에 안전합니다.


답변

Arkadiy에 의한 업데이트 : System.currentTimeMillis()Oracle Java 8의 Windows 7에서 보다 정확한 동작을 관찰했습니다 . 시간은 1 밀리 초 정밀도로 반환되었습니다. OpenJDK의 소스 코드는 변경되지 않았으므로 더 나은 동작을 일으키는 원인을 모르겠습니다.


Sun의 David Holmes는 2 년 전에 Java 타이밍 API (특히 System.currentTimeMillis()System.nanoTime()), 사용시기 및 내부 작동 방식에 대해 자세히 살펴 보는 블로그 기사를 게시했습니다 .

핫스팟 VM 내부 : 시계, 타이머 및 예약 이벤트-1 부-Windows

timed 대기 매개 변수가있는 API에 대해 Windows에서 Java가 사용하는 타이머의 매우 흥미로운 측면 중 하나는 다른 API 호출이 수행 된 내용에 따라 타이머의 해상도가 변경 될 수 있다는 것입니다. . 그는 사용 Thread.sleep()하면이 해상도 변경이 발생 하는 예를 보여줍니다 .


답변

System.nanoTime()이전 JVM에서는 지원되지 않습니다. 그게 걱정이라면currentTimeMillis

정확성과 관련하여 거의 정확합니다. 일부 Windows 컴퓨터에서는currentTimeMillis() 약 10ms (50ms 아님)의 해상도를 갖습니다. 왜 그런지 잘 모르겠지만 일부 Windows 시스템은 Linux 시스템만큼 정확합니다.

나는 과거에 GAGETimer 를 적당한 성공으로 사용했습니다.


답변

다른 사람들이 말했듯이 currentTimeMillis는 일광 절약 시간, 시간 설정 변경, 윤초 및 인터넷 시간 동기화로 인해 변경되는 시계 시간입니다. 앱이 단시간 증가하는 경과 시간 값에 의존하는 경우 대신 nanoTime을 선호 할 수 있습니다.

게임을하는 동안 플레이어가 시간 설정을 다루지 않을 것이라고 생각할 수도 있습니다. 그러나 인터넷 시간 동기화 또는 원격 데스크톱 사용자로 인한 중단을 과소 평가하지 마십시오. nanoTime API는 이러한 종류의 중단에 영향을받지 않습니다.

시계 시간을 사용하고 싶지만 인터넷 시간 동기화로 인한 불연속을 피하려면 Meinberg와 같은 NTP 클라이언트를 고려하십시오. Meinberg는 시계를 주기적으로 재설정하는 대신 클럭 속도를 0으로 조정합니다.

나는 개인적인 경험에서 말합니다. 내가 개발 한 날씨 응용 프로그램에서 무작위로 발생하는 풍속 급증이 발생했습니다. 일반적인 PC에서 시계 시간 동작으로 인해 타임베이스가 중단되고 있음을 깨닫는 데 시간이 걸렸습니다. nanoTime 사용을 시작했을 때 모든 문제가 사라졌습니다. 일관성 (monotonicity)은 원시 정밀도 또는 절대 정확도보다 응용에 더 중요했습니다.


답변

예, 그러한 정밀도가 필요한 경우 사용하십시오 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)); }
    }


답변

게임 그래픽 및 부드러운 위치 업데이트를 위해서는 System.nanoTime()대신System.currentTimeMillis() . 게임에서 currentTimeMillis ()에서 nanoTime ()으로 전환했으며 동작의 부드러움이 크게 개선되었습니다.

1 밀리 초는 이미 정확해야하는 것처럼 보이지만 시각적으로는 그렇지 않습니다. nanoTime()개선 할 수 있는 요소 는 다음과 같습니다.

  • 벽시계 해상도 미만의 정확한 픽셀 위치
  • 원하는 경우 픽셀 간 앤티 앨리어싱 기능
  • Windows 벽시계 부정확
  • 클럭 지터 (벽시계가 실제로 앞으로 움직일 때의 불일치)

다른 답변에서 알 수 있듯이 nanoTime은 반복적으로 호출하면 성능 비용이 발생합니다. 프레임 당 한 번만 호출하고 동일한 값을 사용하여 전체 프레임을 계산하는 것이 가장 좋습니다.