원래 Win32 API 용으로 작성된 게임을 Linux로 포팅하고 있습니다 (Win32 포트의 OS X 포트를 Linux로 포팅).
QueryPerformanceCounter
프로세스가 시작된 이후 uSeconds를 제공하여 구현 했습니다.
BOOL QueryPerformanceCounter(LARGE_INTEGER* performanceCount)
{
gettimeofday(¤tTimeVal, 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 이외의 다른 것으로 이식 할 수 없다는 점은 괜찮습니다.
답변
아마도. 하지만 더 큰 문제가 있습니다. gettimeofday()
타이머를 변경하는 프로세스가 시스템에있는 경우 (예 : ntpd) 잘못된 타이밍이 발생할 수 있습니다. 하지만 “일반”리눅스에서는 해상도 gettimeofday()
가 10us 라고 생각합니다 . 결과적으로 시스템에서 실행되는 프로세스에 따라 앞뒤로 이동할 수 있습니다. 이것은 귀하의 질문에 대한 대답을 효과적으로 만듭니다.
당신은 조사해야 clock_gettime(CLOCK_MONOTONIC)
타이밍 간격을 합니다. 멀티 코어 시스템 및 외부 클럭 설정과 같은 이유로 인해 몇 가지 문제가 적습니다.
또한 clock_getres()
기능을 살펴보십시오 .
답변
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);
}
답변
@남자 이름:
인정해야하는데, 대부분의 예가 내 머리 위로 똑바로 지나갔습니다. 그래도 컴파일되고 작동하는 것 같습니다. 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 물건은 잊어 버려라. 어셈블러는 컴퓨터가 무엇을하고 있는지 알고 싶다면 배워야 할 것이다!”라고 소리 쳤다.
답변
다음에 대한 Linux FAQ에 관심이있을 수 있습니다.clock_gettime(CLOCK_REALTIME)
답변
Wine은 실제로 gettimeofday ()를 사용하여 QueryPerformanceCounter ()를 구현하고 있으며 많은 Windows 게임이 Linux 및 Mac에서 작동하도록하는 것으로 알려져 있습니다.
시작 http://source.winehq.org/source/dlls/kernel32/cpu.c#L312를 .
http://source.winehq.org/source/dlls/ntdll/time.c#L448로 연결됩니다 .
답변
따라서 명시 적으로 마이크로 초라고 말하지만 시스템 클럭의 해상도는 지정되지 않았습니다. 이 맥락에서 해상도는 얼마나 적은 양이 증가 할 것인지를 의미한다고 생각합니다.
데이터 구조는 측정 단위로 마이크로 초를 갖는 것으로 정의되지만, 이것이 클럭이나 운영 체제가 실제로이를 정밀하게 측정 할 수 있다는 의미는 아닙니다.
다른 사람들이 제안한 것처럼 gettimeofday()
시간을 설정하면 시계 왜곡이 발생하고 계산이 중단 될 수 있으므로 나쁘다. clock_gettime(CLOCK_MONOTONIC)
당신이 원하는 clock_getres()
것이고 시계의 정밀도를 알려줄 것입니다.
답변
gettimeofday ()의 실제 해상도는 하드웨어 아키텍처에 따라 다릅니다. Intel 프로세서와 SPARC 시스템은 마이크로 초를 측정하는 고해상도 타이머를 제공합니다. 다른 하드웨어 아키텍처는 일반적으로 100Hz로 설정되는 시스템의 타이머로 대체됩니다. 이러한 경우 시간 해상도가 덜 정확합니다.
이 답변은 고해상도 시간 측정 및 타이머, 파트 I 에서 얻었습니다.