C++에서 CPU 사이클 카운트 측정: __rdtsc
와 플랫폼별 구현법
1. 문제 정의
CPU 사이클 카운트는 성능 측정 및 미세한 코드 최적화에 유용합니다. C++에서 이를 구현하려면 __rdtsc
인트린직 함수나 어셈블리를 활용해야 하며, 플랫폼별 차이를 이해해야 정확한 코드를 작성할 수 있습니다. 이번 글에서는 Windows와 Linux에서 CPU 사이클을 읽는 방법을 설명합니다.
2. rdtsc
명령어란?
rdtsc
(Read Time-Stamp Counter)는 CPU 클럭 사이클을 읽는 x86 명령어입니다. 이 명령은 고속으로 실행되며, CPU의 클럭 주기에 따라 증가하는 값을 반환합니다. 일반적으로 성능 측정 및 벤치마킹에서 사용됩니다.
3. Windows에서 __rdtsc
사용하기
Windows 환경에서는 <intrin.h>
헤더를 통해 __rdtsc
를 바로 사용할 수 있습니다. 이는 어셈블리 코드를 작성하지 않고 CPU 사이클 카운트를 읽는 가장 간단한 방법입니다.
코드 예제:
#include <iostream>
#include <intrin.h> // MSVC 전용 헤더
uint64_t readTSC() {
return __rdtsc(); // CPU 사이클 카운트 반환
}
int main() {
uint64_t start = readTSC(); // 시작 시간
// 측정할 코드
for (int i = 0; i < 1000000; ++i);
uint64_t end = readTSC(); // 종료 시간
std::cout << "Elapsed cycles: " << end - start << std::endl;
return 0;
}
출력 예시:
Elapsed cycles: 12345678
특징:
- 장점: 간단하고 사용이 쉬움.
- 단점: MSVC 전용이며, GCC/Clang에서는 사용 불가.
4. Linux에서 rdtsc
사용하기
Linux에서는 어셈블리 코드를 활용하여 rdtsc
명령어를 사용할 수 있습니다. GNU 확장 어셈블리를 사용해 구현합니다.
코드 예제:
#include <iostream>
#include <stdint.h>
uint64_t readTSC() {
unsigned int lo, hi;
__asm__ __volatile__ (
"rdtsc" // rdtsc 명령어 실행
: "=a" (lo), "=d" (hi) // EAX와 EDX에 결과 저장
);
return ((uint64_t)hi << 32) | lo; // 64비트 값 반환
}
int main() {
uint64_t start = readTSC(); // 시작 시간
// 측정할 코드
for (int i = 0; i < 1000000; ++i);
uint64_t end = readTSC(); // 종료 시간
std::cout << "Elapsed cycles: " << end - start << std::endl;
return 0;
}
출력 예시:
Elapsed cycles: 12345678
특징:
- 장점: 어셈블리를 통해 CPU의 모든 기능을 활용 가능.
- 단점: 플랫폼 종속적이며, 코드 작성이 복잡.
5. 플랫폼 독립적 코드 작성하기
플랫폼에 따라 <intrin.h>
와 <x86intrin.h>
를 조건부로 포함하여 코드의 이식성을 높일 수 있습니다.
코드 예제:
#include <iostream>
#include <stdint.h>
#ifdef _WIN32
#include <intrin.h>
uint64_t readTSC() {
return __rdtsc();
}
#else
#include <x86intrin.h>
uint64_t readTSC() {
unsigned int lo, hi;
__asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi));
return ((uint64_t)hi << 32) | lo;
}
#endif
int main() {
uint64_t start = readTSC();
for (int i = 0; i < 1000000; ++i);
uint64_t end = readTSC();
std::cout << "Elapsed cycles: " << end - start << std::endl;
return 0;
}
특징:
- Windows와 Linux에서 동일한 코드로 실행 가능.
- 조건부 컴파일로 이식성을 보장.
6. 주의사항
- CPU 주파수 변화:
rdtsc
는 CPU의 주파수 변화나 전력 관리 상태의 영향을 받을 수 있습니다. 안정적인 결과를 위해 고정된 클럭 상태에서 테스트하세요. - 멀티코어 환경: TSC 값은 CPU 코어 간에 동기화되지 않을 수 있습니다. 따라서 특정 코어에서만 실행되도록 고정하는 것이 좋습니다.
- 정확도 보장:
lfence
명령어를 사용하여 명령어 순서를 보장할 수 있습니다.
7. 결론
- Windows에서는
__rdtsc
인트린직을 사용하여 간단히 CPU 사이클을 읽을 수 있습니다. - Linux에서는 GNU 어셈블리를 사용해 동일한 작업을 수행할 수 있습니다.
- 플랫폼 독립적인 코드를 작성하려면 조건부 컴파일을 활용하세요.
CPU 사이클 카운트는 코드 성능을 세밀히 분석할 때 유용하며, 정확하고 신뢰할 수 있는 측정을 위해 환경 설정과 주의사항을 반드시 고려해야 합니다.