[java] System.nanoTime ()이 완전히 쓸모 없습니까?

x86 시스템 에서 블로그 게시물 Java의 System.nanoTime ()주의 x86 시스템 에서 설명하는 것처럼 Java의 System.nanoTime ()은 CPU 특정 카운터를 사용하여 시간 값을 반환합니다 . 이제 통화 시간을 측정하는 데 사용하는 다음 사례를 고려하십시오.

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

이제 멀티 코어 시스템에서 time1을 측정 한 후 스레드가 이전 CPU의 카운터보다 작은 다른 프로세서로 스레드가 예약 될 수 있습니다. 따라서 time1보다 작은 time2의 값을 얻을 수 있습니다. 따라서 timeSpent에서 음수 값을 얻습니다.

이 경우를 고려할 때 System.nanotime이 지금은 쓸모가 없습니까?

시스템 시간을 변경해도 나노 시간에는 영향을 미치지 않습니다. 그것은 위에서 설명한 문제가 아닙니다. 문제는 각 CPU가 켜진 이후 다른 카운터를 유지한다는 것입니다. 이 카운터는 첫 번째 CPU에 비해 ​​두 번째 CPU에서 더 낮을 수 있습니다. 스레드가 time1을 얻은 후 OS에 의해 두 번째 CPU로 스케줄 될 수 있으므로 timeSpent 값이 올바르지 않고 음수 일 수도 있습니다.



답변

이 답변은 2011 년 당시 운영 체제에서 실행 된 시간의 Sun JDK가 실제로 한 일의 관점에서 2011 년에 작성되었습니다. 그것은 오래 전이었습니다! leventov의 답변 은보다 최신의 관점을 제공합니다.

그 게시물은 잘못되어 nanoTime안전합니다. Sun의 실시간 및 동시성 사용자 인 David Holmes의 블로그 게시물에 링크되는 게시물에 대한 의견이 있습니다 . 그것은 말한다 :

System.nanoTime ()은 QueryPerformanceCounter / QueryPerformanceFrequency API를 사용하여 구현됩니다. […] QPC에서 사용하는 기본 메커니즘은 하드웨어 추상화 계층 (HAL)에 의해 결정됩니다. […]이 기본값은 하드웨어뿐만 아니라 OS에서도 변경됩니다. 버전. 예를 들어, Windows XP 서비스 팩 2는 SMP 시스템의 다른 프로세서에서 TSC가 동기화되지 않는 문제와 주파수로 인해 TSC (프로세서 타임 스탬프 카운터)가 아닌 전원 관리 타이머 (PMTimer)를 사용하도록 변경했습니다. 전원 관리 설정에 따라 달라질 수 있습니다 (따라서 경과 시간과의 관계).

그래서, Windows에서,이 이었다 WINXP SP2까지 문제가 있지만 지금은 아니다.

다른 플랫폼에 대해 이야기하는 II 부 (또는 그 이상)를 찾을 수 없지만이 기사에는 Linux가 같은 방식으로 동일한 문제를 발견하고 해결했다는 언급이 포함되어 있습니다. clock_gettime (CLOCK_REALTIME)에 대한 FAQ 링크 라고 말합니다.

  1. clock_gettime (CLOCK_REALTIME)이 모든 프로세서 / 코어에서 일관성이 있습니까? (아키 문제가 있습니까? 예 : ppc, arm, x86, amd64, sparc).

그것은 해야 되거나 버그 간주.

그러나 x86 / x86_64에서는 동기화되지 않거나 가변 주파수 TSC로 인해 시간 불일치가 발생할 수 있습니다. 2.4 커널은 실제로 이것에 대한 보호 기능이 없었으며 초기 2.6 커널은 여기서도 잘하지 못했습니다. 2.6.18부터는 이것을 감지하는 논리가 더 좋으며 일반적으로 안전한 클럭 소스로 돌아갑니다.

ppc에는 항상 동기화 된 타임베이스가 있으므로 문제가되지 않습니다.

따라서 Holmes의 링크가 해당 nanoTime호출 을 암시하는 것으로 읽을 수 있다면 clock_gettime(CLOCK_REALTIME)x86의 커널 2.6.18과 항상 PowerPC에서 안전합니다 (Intel과 달리 IBM과 Motorola는 실제로 마이크로 프로세서를 설계하는 방법을 알고 있기 때문에).

슬프게도 SPARC 또는 Solaris에 대한 언급은 없습니다. 물론 IBM JVM이 무엇을하는지 전혀 모릅니다. 그러나 최신 Windows 및 Linux의 Sun JVM이이 권리를 얻습니다.

편집 :이 답변은 인용 소스를 기반으로합니다. 그러나 나는 그것이 실제로 완전히 잘못되었을 수도 있다고 여전히 걱정합니다. 좀 더 최신 정보는 정말 귀중 할 것입니다. 난 그냥에 대한 링크를 통해 온 리눅스의 시계에 대한 사년 새로운 기사 유용 할 수있다.


답변

나는 약간의 검색을했는데 하나가 pedantic이라면 예를 들어 쓸모없는 것으로 간주 될 수 있습니다 … 특정 상황에서 … 필요한 시간이 얼마나 민감한 지에 달려 있습니다 …

Java Sun 사이트 에서이 인용문 을 확인하십시오 .

실시간 클록과 System.nanoTime ()은 모두 동일한 시스템 호출을 기반으로하므로 동일한 클록을 기반으로합니다.

Java RTS를 사용하면 모든 시간 기반 API (예 : 타이머, 정기 스레드, 데드 라인 모니터링 등)가 고해상도 타이머를 기반으로합니다. 또한 실시간 우선 순위와 함께 실시간 제약 조건에 적합한 코드가 적시에 실행되도록 할 수 있습니다. 대조적으로, 일반 Java SE API는 특정 시간에 실행을 보장하지 않고 고해상도 시간을 처리 할 수있는 몇 가지 메소드 만 제공합니다. 경과 시간 측정을 수행하기 위해 코드의 여러 지점간에 System.nanoTime ()을 사용하면 항상 정확해야합니다.

Java는 또한 nanoTime () 메소드에 대한주의 사항이 있습니다 .

이 방법은 경과 시간을 측정하는 데만 사용할 수 있으며 다른 시스템 또는 벽시계 시간 개념과 관련이 없습니다. 반환 된 값은 고정되었지만 임의의 시간 (나중에 값이 음수 일 수 있음) 이후 나노초를 나타냅니다. 이 방법은 나노초 정밀도를 제공하지만 반드시 나노초 정확도는 아닙니다. 값이 얼마나 자주 변경되는지는 보증하지 않습니다. 약 292.3 년 (2 63
나노초)을 초과하는 연속 호출의 차이는 숫자 오버플로로 인해 경과 시간을 정확하게 계산하지 않습니다.

그릴 수있는 유일한 결론은 nanoTime ()을 정확한 값으로 신뢰할 수 없다는 것입니다. 따라서, 단지 나노초 간격의 시간을 측정 할 필요가 없으면 결과 리턴 값이 음수 인 경우에도이 방법으로 충분합니다. 그러나 더 높은 정밀도가 필요한 경우 JAVA RTS를 사용하는 것이 좋습니다.

따라서 귀하의 질문에 대답하기 위해 … no nanoTime ()은 쓸모가 없습니다 …. 모든 상황에서 사용하는 가장 신중한 방법은 아닙니다.


답변

토론 할 필요없이 소스 만 사용하십시오. 다음은 Linux 용 SE 6이며 자신의 결론을 내립니다.

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


답변

Java 7부터는 System.nanoTime()JDK 사양으로 안전합니다. System.nanoTime()Javadoc 은 JVM 내에서 (즉, 모든 스레드에서) 관찰 된 모든 호출이 단조롭다는 것을 분명히합니다.

반환 된 값은 고정되었지만 임의의 원점 시간 (나중에 값이 음수 일 수 있음) 이후 나노초를 나타냅니다. Java 가상 머신의 인스턴스에서이 메소드의 모든 호출에서 동일한 오리진이 사용됩니다. 다른 가상 머신 인스턴스는 다른 오리진을 사용할 수 있습니다.

JVM / JDK 구현은 기본 OS 유틸리티가 호출 될 때 관찰 될 수있는 불일치를 해결하는 역할을합니다 (예 : Tom Anderson의 답변 에서 언급 된 ).

이 질문에 대한 다른 이전 답변의 대부분 (2009–2012 년에 작성)은 아마도 Java 5 또는 Java 6과 관련이 있지만 더 이상 최신 버전의 Java와 관련이없는 FUD를 표현합니다.

그러나 JDK 보증 nanoTime()의 안전 에도 불구하고 OpenJDK에는 특정 플랫폼이나 특정 상황 (예 : JDK-8040140 , JDK-8184271 ) 에서이 보증을 유지하지 못하도록하는 몇 가지 버그가있었습니다 . 현재 OpenJDK wrt에는 공개 (알려진) 버그가 nanoTime()없지만, 새로운 OpenJDK 릴리스에서 새로운 버그 나 회귀가 발견되면 누구에게도 충격을주지 않아야합니다.

이를 염두에두고, 시간 제한 차단, 간격 대기, 시간 초과 등에 사용 nanoTime()되는 코드는 예외를 발생시키는 것이 아니라 음의 시간 차이 (시간 초과)를 0으로 처리하는 것이 바람직합니다. 그것은 모든 클래스의 모든 시간 제한 대기 방법의 동작과 일치하기 때문에이 방법은 바람직하다 java.util.concurrent.*, 예를 들어 Semaphore.tryAcquire(), Lock.tryLock(), BlockingQueue.poll(), 등

그럼에도 불구하고, nanoTime()시간 차단, 인터벌 대기, 타임 아웃 등을 구현하기 위해 여전히 선호되어야한다. currentTimeMillis()왜냐하면 후자는 “시간이 거꾸로 돌아가는”현상 (예 : 서버 시간 수정으로 인해)에 영향을 받기 때문이다. 즉, currentTimeMillis()시간 간격 측정에 적합하지 않기 때문이다. 조금도. 자세한 내용은 이 답변 을 참조하십시오.

사용하는 대신 nanoTime()직접적으로 코드 실행 시간 측정을 위해 전문 벤치마킹 체계와 프로파일은 바람직하게는 예를 들면, 표기 JMH비동기 프로파일벽 프로파일 클록 모드 .


답변

면책 조항 : 나는이 도서관의 개발자입니다

당신은 이것을 더 좋아할 것입니다 :

http://juliusdavies.ca/nanotime/

그러나 DLL 또는 Unix .so (공유 객체) 파일을 현재 사용자의 홈 디렉토리에 복사하여 JNI를 호출 할 수 있습니다.

일부 배경 정보는 다음 사이트의 내 사이트에 있습니다.

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


답변

Linux는 CPU 간의 불일치를 수정하지만 Windows는 그렇지 않습니다. System.nanoTime ()이 약 1 마이크로 초까지만 정확하다고 가정합니다. 더 긴 타이밍을 얻는 간단한 방법은 foo ()를 1000 번 이상 호출하고 시간을 1000으로 나누는 것입니다.


답변

절대 쓸모가 없습니다. 타이밍 애호가들은 멀티 코어 문제를 정확하게 지적하지만 실제 응용 프로그램에서는 종종 currentTimeMillis ()보다 근본적으로 좋습니다.

프레임 새로 고침에서 그래픽 위치를 계산할 때 nanoTime ()은 내 프로그램에서 훨씬 부드러운 움직임을 유발합니다.

그리고 나는 멀티 코어 머신에서만 테스트합니다.