rdtsc前调用cpuid防乱序的实现方法与调用位置咨询
问题原因说明
你遇到的时间戳差值为负的问题主要来自两个核心原因:
- 处理器乱序执行机制导致两次
rdtsc指令的实际执行顺序和代码编写顺序不一致,后调用的rdtsc可能被提前调度,返回更小的时间戳 - 现有封装代码存在数据类型错误:将存储时间戳高32位的
d强制转换为uint16_t会直接丢弃其高16位,当时间戳数值超过2^48时会出现溢出错误,也会引发异常差值
cpuid调用实现方案
cpuid是x86平台的序列化指令,执行时会等待所有此前的指令全部执行完成后,才会执行后续指令,可以精准控制rdtsc的执行顺序。
建议直接将cpuid逻辑添加到rdtsc函数内部,无需每次调用时在主函数手动添加,复用性更高,也能避免漏加导致的错误。需要注意cpuid执行时会修改eax、ebx、ecx、edx四个通用寄存器,需要在内联汇编的破坏列表中声明,避免编译器寄存器分配冲突。
修正后的完整rdtsc封装代码如下:
#include <stdint.h> inline uint64_t rdtsc() { uint32_t a, d; // 先执行cpuid序列化,保证此前所有指令执行完成后再采样时间戳 asm volatile ("cpuid\n\t" "rdtsc\n\t" : "=a"(a), "=d"(d) : : "ebx", "ecx"); // 修复原代码的类型转换错误,将高32位左移32位后拼接低32位 return ((uint64_t)d << 32) | a; }
如果是测量某段代码的执行耗时,为了保证要测量的代码不会被乱序到结束采样的rdtsc之后,结束采样建议搭配rdtscp指令使用,写法如下:
uint32_t a, d; uint64_t start = rdtsc(); // 此处放置需要测量耗时的代码段 asm volatile ("rdtscp\n\t" "cpuid\n\t" : "=a"(a), "=d"(d) : : "ebx", "ecx"); uint64_t end = ((uint64_t)d << 32) | a; uint64_t cost = end - start;
内容的提问来源于stack exchange,提问作者MitandGrit
相关产品推荐
相关产品推荐

