首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> VC 教學

VC++ 內嵌組語使用 RDTSC 精確測量程式碼執行週期

     編輯:VC 教學

現代 x86/x64 CPU 內建 64-bit 時間戳計數器 (TSC),以 CPU 核心頻率遞增。使用 RDTSC (Read Time-Stamp Counter) 指令可在使用者模式以極低開銷 (<10 cycles) 讀取當前計數值,適用於微基準測試、效能分析、偵測虛擬化/除錯器延遲。

一、RDTSC 基本用法

// 內嵌組語 (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 與序列化

// 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)週期精確需核心驅動/權限深度效能分析、熱點分析、分支預測失敗統計

六、實務建議

  1. 測量前 SetThreadAffinityMask(GetCurrentThread(), 1) 鎖定核心 0。
  2. 預熱:先執行數次被測函式填充指令/資料快取。
  3. 重複測量 1000+ 次取中位數,排除 OS 抽插、中斷干擾。
  4. 關閉超執緒、測試期間固定 CPU 頻率 (BIOS/電源計畫)。
  5. 針對函式層級分析,優先使用 Visual Studio Profiler/VTune/perf 等專業工具。

RDTSC 是效能工程師工具箱中的「手術刀」——銳利但需精準操作。善用內建函式 __rdtsc/__rdtscp 取代手寫組語,配合親和性鎖定與統計方法,可獲得可信度極高的微基準數據。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved