gettimeofday ()는 마이크로 초 해상도가 보장됩니까?


97

원래 Win32 API 용으로 작성된 게임을 Linux로 포팅하고 있습니다 (Win32 포트의 OS X 포트를 Linux로 포팅).

QueryPerformanceCounter프로세스가 시작된 이후 uSeconds를 제공하여 구현 했습니다.

BOOL QueryPerformanceCounter(LARGE_INTEGER* performanceCount)
{
    gettimeofday(&currentTimeVal, NULL);
    performanceCount->QuadPart = (currentTimeVal.tv_sec - startTimeVal.tv_sec);
    performanceCount->QuadPart *= (1000 * 1000);
    performanceCount->QuadPart += (currentTimeVal.tv_usec - startTimeVal.tv_usec);

    return true;
}

이것은 QueryPerformanceFrequency()주파수로 상수 1000000 을 제공하는 것과 함께 내 컴퓨터 에서 잘 작동 uSeconds하여 프로그램 시작 이후에 포함 된 64 비트 변수를 제공합니다.

그래서 이것은 휴대용입니까? 커널이 특정 방식이나 그와 비슷한 방식으로 컴파일 된 경우 다르게 작동하는 것을 발견하고 싶지 않습니다. 그러나 Linux 이외의 다른 것으로 이식 할 수 없다는 점은 괜찮습니다.

답변:


57

아마도. 하지만 더 큰 문제가 있습니다. gettimeofday()타이머를 변경하는 프로세스가 시스템에있는 경우 (예 : ntpd) 잘못된 타이밍이 발생할 수 있습니다. 하지만 "일반"리눅스에서는 해상도 gettimeofday()가 10us 라고 생각합니다 . 결과적으로 시스템에서 실행되는 프로세스에 따라 앞뒤로 이동할 수 있습니다. 이것은 귀하의 질문에 대한 대답을 효과적으로 만듭니다.

당신은 조사해야 clock_gettime(CLOCK_MONOTONIC)타이밍 간격을 합니다. 멀티 코어 시스템 및 외부 클럭 설정과 같은 이유로 인해 몇 가지 문제가 적습니다.

또한 clock_getres()기능을 살펴보십시오 .


1
clock_gettime은 최신 Linux에만 있습니다. 다른 시스템에는 gettimeofday () 만 있습니다
vitaly.v.ch 2009

3
@ vitaly.v.ch 그것은 POSIX이므로 Linux 전용이 아니고 '초보자'입니까? Red Hat Enterprise Linux와 같은 'Enterprise'배포판조차도 clock_gettime이있는 2.6.18을 기반으로합니다. 따라서 새로운 것은 아닙니다. 정말 오래된 커널에 대해 이야기하는 것 WTF 의미합니까?
Spudd86

clock_gettime은 2001 년 POSIX에 포함되었습니다. 내가 아는 한 현재 clock_gettime ()은 Linux 2.6과 qnx에서 구현되었습니다. 그러나 리눅스 2.4는 현재 많은 생산 시스템에서 사용되고 있습니다.
vitaly.v.ch 2010-06-25

2001 년에 도입되었지만 POSIX 2008까지 필수 사항은 아닙니다.
R .. GitHub STOP HELPING ICE

2
lock_gettime에 대한 Linux FAQ에서 (David Schlosnagle의 답변 참조) "CLOCK_MONOTONIC ... 주파수는 adjtimex ()를 통해 NTP에 의해 조정됩니다. 앞으로 (나는 여전히 패치를 가져 오려고합니다) CLOCK_MONOTONIC_RAW가 될 것입니다. 전혀 수정되지 않으며 하드웨어 카운터와 선형 상관 관계를 갖습니다. " 나는 _RAW 시계가 그것을 커널로 만들지 않았다고 생각한다 (이름이 _HR로 바뀌지 않는 한, 내 연구에 따르면 노력도 포기된다고한다).
Tony Delroy 2011-06-15

41

Intel 프로세서를위한 고해상도, 낮은 오버 헤드 타이밍

Intel 하드웨어를 사용하는 경우 CPU 실시간 명령 카운터를 읽는 방법은 다음과 같습니다. 프로세서가 부팅 된 이후 실행 된 CPU주기 수를 알려줍니다. 성능 측정을 위해 얻을 수있는 가장 세밀한 카운터 일 것입니다.

이것은 CPU 사이클 수입니다. 리눅스에서는 / proc / cpuinfo에서 CPU 속도를 구하고 나눌 수 있습니다. 이것을 double로 변환하는 것은 매우 편리합니다.

내 상자에서 이것을 실행하면

11867927879484732
11867927879692217
it took this long to call printf: 207485

다음 은 수많은 세부 정보를 제공 하는 인텔 개발자 가이드 입니다.

#include <stdio.h>
#include <stdint.h>

inline uint64_t rdtsc() {
    uint32_t lo, hi;
    __asm__ __volatile__ (
      "xorl %%eax, %%eax\n"
      "cpuid\n"
      "rdtsc\n"
      : "=a" (lo), "=d" (hi)
      :
      : "%ebx", "%ecx");
    return (uint64_t)hi << 32 | lo;
}

main()
{
    unsigned long long x;
    unsigned long long y;
    x = rdtsc();
    printf("%lld\n",x);
    y = rdtsc();
    printf("%lld\n",y);
    printf("it took this long to call printf: %lld\n",y-x);
}

11
TSC는 코어간에 항상 동기화되지 않을 수 있으며 프로세서가 저전력 모드로 전환 될 때 주파수를 중지하거나 변경할 수 있으며 일반적으로 항상 신뢰할 수있는 것은 아닙니다. 커널은 신뢰할 수있는시기를 감지하고 HPET 및 ACPI PM 타이머와 같은 다른 대안을 감지하고 자동으로 최상의 것을 선택할 수 있습니다. TSC가 안정적이고 단조 롭다고 확신하지 않는 한 타이밍을 위해 항상 커널을 사용하는 것이 좋습니다.
CesarB

12
Core 이상 Intel 플랫폼의 TSC는 여러 CPU에서 동기화 되고 전원 관리 상태와 관계없이 일정한 주파수로 증가합니다. 인텔 소프트웨어 개발자 설명서, Vol. 3 섹션 18.10. 그러나 카운터가 증가하는 속도 는 CPU의 주파수와 동일 하지 않습니다 . TSC는 "확장 가능한 버스 주파수 및 최대 해결 된 버스 비율의 제품과 동일한 플랫폼의 최대 해결 주파수"에서 증가합니다. Intel Software Developer 's Manual, Vol. 3 섹션 18.18.5. 이러한 값은 CPU의 모델 별 레지스터 (MSR)에서 가져옵니다.
sstock

7
다음과 같이 CPU의 모델 별 레지스터 (MSR)를 쿼리하여 확장 가능한 버스 주파수 및 최대 확인 된 버스 비율을 얻을 수 있습니다. 확장 가능한 버스 주파수 == MSR_FSB_FREQ [2 : 0] id 0xCD, 최대 확인 된 버스 비율 == MSR_PLATFORM_ID [12 : 8] id 0x17. 레지스터 값을 해석하려면 Intel SDM Vol.3 부록 B.1을 참조하십시오. Linux에서 msr-tools를 사용하여 레지스터를 쿼리 할 수 ​​있습니다. kernel.org/pub/linux/utils/cpu/msr-tools
sstock

1
CPUID첫 번째 RDTSC명령 이후 와 벤치 마크중인 코드를 실행하기 전에 코드를 다시 사용해야하지 않습니까? 그렇지 않으면 벤치 마크 된 코드가 첫 번째와 병렬로 실행되기 전 / 병렬로 실행 RDTSC되고 결과적으로 RDTSC델타 에서 과소 표현 되는 것을 막을 수 있습니까?
Tony Delroy 2011-06-15

18

@남자 이름:

인정해야하는데, 대부분의 예가 내 머리 위로 똑바로 지나갔습니다. 그래도 컴파일되고 작동하는 것 같습니다. SMP 시스템 또는 SpeedStep에 안전합니까?

좋은 질문입니다. 코드는 괜찮은 것 같습니다. 실용적인 관점에서 우리는 매일 회사에서 사용하고 있으며 2 ~ 8 개의 코어에서 모두 매우 다양한 상자에서 실행됩니다. 물론 YMMV 등은 신뢰할 수 있고 오버 헤드가 낮은 것 같습니다 (컨텍스트를 시스템 공간으로 전환하지 않기 때문에).

일반적으로 작동 방식은 다음과 같습니다.

  • 코드 블록을 어셈블러로 선언합니다 (그리고 휘발성이므로 최적화 프로그램은 그대로 둡니다).
  • CPUID 명령을 실행합니다. CPU 정보 (아무것도하지 않음)를 얻는 것 외에도 CPU의 실행 버퍼를 동기화하여 타이밍이 비 순차적 실행의 영향을받지 않도록합니다.
  • rdtsc (읽기 타임 스탬프) 실행을 실행합니다. 이것은 프로세서가 재설정 된 이후 실행 된 머신 사이클 수를 가져옵니다. 이것은 64 비트 값이므로 현재 CPU 속도에서는 약 194 년마다 랩핑됩니다. 흥미롭게도 원래 펜티엄 참조에서 그들은 약 5800 년마다 랩핑을한다는 점을 지적합니다.
  • 마지막 두 줄은 레지스터의 값을 hi 및 lo 변수에 저장하고 64 비트 반환 값에 넣습니다.

특정 참고 사항 :

  • 비 순차적 실행은 잘못된 결과를 초래할 수 있으므로 cpu에 대한 일부 정보를 제공하는 것 외에도 비 순차적 명령 실행을 동기화하는 "cpuid"명령어를 실행합니다.

  • 대부분의 OS는 시작될 때 CPU의 카운터를 동기화하므로 몇 나노초 이내에 대답하는 것이 좋습니다.

  • 최대 절전 모드 설명은 사실 일 수 있지만 실제로는 최대 절전 모드 경계를 넘는 타이밍에 대해 신경 쓰지 않을 것입니다.

  • 속도 단계 관련 : 최신 Intel CPU는 속도 변화를 보상하고 조정 된 카운트를 반환합니다. 네트워크에있는 일부 상자를 빠르게 스캔 한 결과없는 상자 하나만 발견했습니다. 오래된 데이터베이스 서버를 실행하는 펜티엄 3입니다. (이것들은 리눅스 박스이므로 grep constant_tsc / proc / cpuinfo로 확인했습니다)

  • AMD CPU에 대해 잘 모르겠습니다. 우리는 주로 Intel 상점입니다. 비록 저수준 시스템 전문가 중 일부가 AMD 평가를 수행했음을 알고 있습니다.

이것이 당신의 호기심을 충족시키기를 바랍니다. 그것은 흥미롭고 (IMHO) 프로그래밍의 학습이 부족한 영역입니다. 프로그래머가 C를 알아야하는지 여부에 대해 Jeff와 Joel이 이야기 할 때 알고 있습니까? 나는 그들에게 "고급 C 물건은 잊어 버려라. 어셈블러는 컴퓨터가 무엇을하고 있는지 알고 싶다면 배워야 할 것이다!"라고 소리 쳤다.


1
... 커널 사람들은 사람들이 rdtsc 사용을 잠시 중단하도록하려고 노력해 왔습니다. 그리고 일반적으로 커널에서 사용하는 것은 그다지 신뢰할 수 없기 때문에 피합니다.
Spudd86

1
참고로, 제가 물은 질문 (별도의 답변-주석 앞에)은 다음과 같습니다. "저는 인정해야합니다. 대부분의 예제가 제 머리 위로 똑바로 넘어갔습니다.하지만 컴파일을하고 작동하는 것 같습니다. 이것이 안전한가요? SMP 시스템 또는 SpeedStep? "
Bernard



9

따라서 명시 적으로 마이크로 초라고 말하지만 시스템 클럭의 해상도는 지정되지 않았습니다. 이 맥락에서 해상도는 얼마나 적은 양이 증가 할 것인지를 의미한다고 생각합니다.

데이터 구조는 측정 단위로 마이크로 초를 갖는 것으로 정의되지만, 이것이 클럭이나 운영 체제가 실제로이를 정밀하게 측정 할 수 있다는 의미는 아닙니다.

다른 사람들이 제안한 것처럼 gettimeofday()시간을 설정하면 시계 왜곡이 발생하고 계산이 중단 될 수 있으므로 나쁘다. clock_gettime(CLOCK_MONOTONIC)당신이 원하는 clock_getres()것이고 시계의 정밀도를 알려줄 것입니다.


그렇다면 gettimeofday ()가 일광 절약 제로 앞뒤로 점프하면 코드에서 어떤 일이 발생합니까?
mpez0

3
clock_gettime은 최신 Linux에만 있습니다. 다른 시스템에는 gettimeofday () 만 있습니다
vitaly.v.ch 2009

8

gettimeofday ()의 실제 해상도는 하드웨어 아키텍처에 따라 다릅니다. Intel 프로세서와 SPARC 시스템은 마이크로 초를 측정하는 고해상도 타이머를 제공합니다. 다른 하드웨어 아키텍처는 일반적으로 100Hz로 설정되는 시스템의 타이머로 대체됩니다. 이러한 경우 시간 해상도가 덜 정확합니다.

이 답변은 고해상도 시간 측정 및 타이머, 파트 I 에서 얻었습니다.


6

이 답변 은 시계가 조정되는 문제를 언급합니다. 틱 단위를 보장하는 문제와 조정되는 시간 문제는 모두 C ++ 11에서<chrono> 라이브러리 .

시계 std::chrono::steady_clock 는 조정되지 않음을 보장하며 또한 실시간에 비해 일정한 속도로 진행되므로 SpeedStep과 같은 기술이 시계에 영향을주지 않아야합니다.

std::chrono::duration와 같은 전문화 중 하나로 변환하여 형식 안전 단위를 얻을 수 있습니다 std::chrono::microseconds. 이 유형을 사용하면 눈금 값에 사용되는 단위에 대한 모호성이 없습니다. 그러나 시계에 반드시이 해상도가있는 것은 아닙니다. 실제로 정확한 시계를 갖지 않고도 기간을 attoseconds로 변환 할 수 있습니다.


4

내 경험과 인터넷을 통해 읽은 내용에서 대답은 "아니오"입니다. 보장되지 않습니다. CPU 속도, 운영 체제, Linux 버전 등에 따라 다릅니다.


3

RDTSC를 읽는 것은 SMP 시스템에서 신뢰할 수 없습니다. 각 CPU는 자체 카운터를 유지하고 각 카운터는 다른 CPU와 동기화되어 보장되지 않기 때문입니다.

나는 시도하는 것이 좋습니다 clock_gettime(CLOCK_REALTIME). posix 매뉴얼은 이것이 모든 호환 시스템에서 구현되어야 함을 나타냅니다. 나노초 카운트를 제공 할 수 있지만 clock_getres(CLOCK_REALTIME)실제 해상도가 무엇인지 확인 하기 위해 시스템을 확인하고 싶을 것입니다 .


clock_getres(CLOCK_REALTIME)실제 해상도를 제공하지 않습니다. 시간 타이머를 사용할 수있을 때 항상 "1ns"(1 나노초)를 반환 include/linux/hrtimer.h합니다. 파일을 확인 하십시오 define HIGH_RES_NSEC 1(자세한 내용은 stackoverflow.com/a/23044075/196561 )
osgx 2014
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.