現代 x86/x64 CPU 內建 64-bit 時間戳計數器 (TSC),以 CPU 核心頻率遞增。使用 RDTSC (Read Time-Stamp Counter) 指令可在使用者模式以極低開銷 (<10 cycles) 讀取當前計數值,適用於微基準測試、效能分析、偵測虛擬化/除錯器延遲。
// 內嵌組語 (MSVC 32-bit)
unsigned long long ReadTSC() {
unsigned int low, high;
__asm {
rdtsc
mov low, eax
mov high, edx
}
return ((unsigned long long)high << 32) | low;
}
64-bit 模式下 RDTSC 直接回傳於 RAX,MSVC 提供編譯器內建函式:
#include <intrin.h>
#pragma intrinsic(__rdtsc)
unsigned long long tsc = __rdtsc(); // 跨平台、無需組語
#include <iostream>
#include <intrin.h>
#include <windows.h>
using namespace std;
int main() {
// 取得 CPU 頻率 (Hz) - 簡易法:QueryPerformanceFrequency 不等於 CPU 頻率!
// 正確做法:WMI 查詢、CPUID leaf 0x15/0x16、或 /proc/cpuinfo (Linux)
// 此處假設 3.0 GHz 示範
const double cpuFreqGHz = 3.0;
unsigned long long start = __rdtsc();
// 被測程式碼
volatile int sum = 0;
for (int i = 0; i < 1000000; ++i) sum += i;
unsigned long long end = __rdtsc();
unsigned long long cycles = end - start;
double seconds = cycles / (cpuFreqGHz * 1e9);
cout << "Cycles: " << cycles << "\n";
cout << "Time: " << seconds * 1e6 << " us\n";
cout << "Sum: " << sum << " (防止優化移除)\n";
return 0;
}
| 問題 | 說明 | 對策 |
|---|---|---|
| Out-of-order 執行 | CPU 可能重排指令,RDTSC 不保證序列化 | 使用 RDTSCP (序列化讀取) 或前置 CPUID (完整序列化,但破壞暫存器) |
| 多核心 TSC 不同步 | 各核心 TSC 可能不同步,執行緒遷移導致數值倒退 | SetThreadAffinityMask 綁定單核心;現代 OS/硬體多支援 Invariant TSC (恆定頻率、跨核心同步) |
| 變頻技術 | SpeedStep/Turbo Boost 導致 TSC 頻率變動 | Invariant TSC 以基準頻率遞增;或改用 QueryPerformanceCounter (HPET/ACPI PM Timer) |
| 編譯器優化移除程式碼 | 無副作用迴圈被刪除 | volatile、輸出結果、#pragma optimize("", off) |
| OS 截斷/虛擬化 | Hypervisor 可能攔截 RDTSC、注入延遲 | 偵測虛擬化環境、改用 PMU (Performance Monitoring Unit) 事件計數器 |
// RDTSCP: 讀取 TSC 並取得 IA32_TSC_AUX (核心 ID),語意上序列化
unsigned long long ReadTSCP(unsigned int* pCoreId = nullptr) {
unsigned int low, high, aux;
__asm {
rdtscp
mov low, eax
mov high, edx
mov aux, ecx
}
if (pCoreId) *pCoreId = aux;
return ((unsigned long long)high << 32) | low;
}
| API | 精度 | 開銷 | 適用場景 |
|---|---|---|---|
| RDTSC / __rdtsc | ~0.3 ns (3 GHz) | ~10-30 cycles | 微基準、極短程式碼段、偵測除錯器 |
| QueryPerformanceCounter | ~100 ns - 1 us | ~500-2000 cycles (系統呼叫) | 毫秒級以上、跨執行緒/進程、生產環境計時 |
| std::chrono::high_resolution_clock | 平台相關 | C++ 封裝開銷 | 可移植程式碼、C++11+ 專案 |
| PMU (perf_event_open, Intel PT) | 週期精確 | 需核心驅動/權限 | 深度效能分析、熱點分析、分支預測失敗統計 |
SetThreadAffinityMask(GetCurrentThread(), 1) 鎖定核心 0。Visual Studio Profiler/VTune/perf 等專業工具。RDTSC 是效能工程師工具箱中的「手術刀」——銳利但需精準操作。善用內建函式 __rdtsc/__rdtscp 取代手寫組語,配合親和性鎖定與統計方法,可獲得可信度極高的微基準數據。