如何将单线程RDTSC性能测量函数修改为适配OpenMP多线程版本
多线程性能测量适配修改方案
原有单线程版本的不兼容问题
- 进程亲和性强制绑定到单个核心:
SetProcessAffinityMask(GetCurrentProcess(), 1)会将整个进程的所有线程都限制在CPU 0上运行,完全抵消多线程并行收益,是首先要删除的逻辑 - 全局进程优先级重复设置:
SetPriorityClass属于进程级配置,重复调用无意义,只需在程序初始化阶段执行一次即可,不需要放在每次测量启动逻辑里 - 时间戳存储为单变量:多线程场景下每个线程需要独立存储自己的起始/结束时间戳,避免线程间读写冲突
适配OpenMP的测量代码
#include <Windows.h> #include <intrin.h> #include <omp.h> typedef unsigned __int64 prof_time_t; // 全局初始化仅在程序启动时调用1次,禁止放在线程内执行 void init_measure_env() { // 仅初始化时设置一次进程高优先级 SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS); // 恢复进程可用所有CPU核心,删除原单核心绑定逻辑 DWORD_PTR process_affinity, system_affinity; GetProcessAffinityMask(GetCurrentProcess(), &process_affinity, &system_affinity); SetProcessAffinityMask(GetCurrentProcess(), system_affinity); } void inline start_measure(prof_time_t & cycle_read) { int a[4], b = 1; // 序列化指令流,保证前面的指令执行完成后再读取时间戳 __cpuid(a, b); cycle_read = __rdtsc(); } void inline end_measure(prof_time_t & cycle_read) { unsigned int aux; prof_time_t end_cycle = __rdtscp(&aux); int a[4], b = 1; __cpuid(a, b); // 直接返回耗时差值,也可根据使用习惯调整为单独返回结束时间戳 cycle_read = end_cycle - cycle_read; }
OpenMP场景使用示例
int main() { // 首先执行全局环境初始化 init_measure_env(); prof_time_t total_cycle; // 测量整个并行段的总耗时 start_measure(total_cycle); #pragma omp parallel { // 声明为线程私有变量,避免多线程读写冲突 prof_time_t thread_cycle; start_measure(thread_cycle); // 此处替换为需要测量的多线程业务代码 // ... end_measure(thread_cycle); // 临界区输出每个线程的执行耗时,避免打印混乱 #pragma omp critical { printf("线程ID:%d,执行周期数:%llu\n", omp_get_thread_num(), thread_cycle); } } end_measure(total_cycle); printf("整个并行段总执行周期数:%llu\n", total_cycle); return 0; }
测量注意事项
- TSC同步问题:Windows平台下现代CPU默认已开启恒定TSC(Invariant TSC),不同核心的TSC基准是全局同步的,跨核心读取的时间戳可以直接做差值计算,无需额外适配
- 可选绑核优化:如果需要进一步降低线程调度切换带来的测量误差,可以在OpenMP线程内调用
SetThreadAffinityMask将每个线程绑定到固定CPU核心 - 最小测量粒度:
__cpuid和__rdtsc本身有几十周期的执行开销,被测逻辑执行时间不要过短,否则测量误差占比会过高
内容的提问来源于stack exchange,提问作者Megan Darcy
相关产品推荐
相关产品推荐

