Vai*_*xit 2 c++ gcc cpu-architecture memory-barriers rdtsc
我正在尝试将clock_gettime(CLOCK_REALTIME, &ts) 替换为rdtsc,以CPU 周期而不是服务器时间来衡量代码执行时间。基准测试代码的执行时间对于软件至关重要。我尝试在独立核心上的 x86_64 3.20GHz ubuntu 机器上运行代码并得到以下数字:
情况 1:时钟获取时间: 24 纳秒
void gettime(Timespec &ts) {
clock_gettime(CLOCK_REALTIME, &ts);
}
Run Code Online (Sandbox Code Playgroud)
情况 2:rdtsc(没有 mfence 和编译器屏障): 10 ns
void rdtsc(uint64_t& tsc) {
unsigned int lo,hi;
__asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi));
tsc = ((uint64_t)hi << 32) | lo;
}
Run Code Online (Sandbox Code Playgroud)
情况 3:rdtsc(带有 mfence 和编译器屏障): 30 ns
void rdtsc(uint64_t& tsc) {
unsigned int lo,hi;
__asm__ __volatile__ ("mfence;rdtsc" : "=a" (lo), "=d" (hi) :: "memory");
tsc = ((uint64_t)hi << 32) | lo;
}
Run Code Online (Sandbox Code Playgroud)
这里的问题是我知道 rdtsc 是非序列化调用,可以由 CPU 重新排序,另一种选择是 rdtscp,它是序列化调用,但 rdtscp 调用之后的指令可以在 rdtscp 调用之前重新排序。使用内存屏障会增加执行时间。
您想要lfence;rdtsc启动时钟并rdtscp;lfence停止时钟,因此障碍物位于定时间隔之外。
(或者有时您想lfence;rdtsc;lfence启动时钟,以获得额外的可重复性,但代价是更多的开销。)
MFENCE 是错误的指令;它不能保证序列化指令流(但实际上它在具有最新微代码的 Skylake 上可以序列化,以修复错误)。LFENCE 序列化指令流,无需等待存储缓冲区变空,只用于 ROB。这在 Intel 上始终如此,但在 AMD 上仅在启用 Spectre 缓解的情况下才产生lfenceNOP。(我猜 AMD 不会movntdqa对 WC 内存中的加载进行重新排序,因此lfence作为内存屏障毫无意义,仅作为针对推测执行或 RDTSC 的执行屏障有用。)
另请参阅如何从 C++ 获取 x86_64 中的 CPU 周期计数?其中有一个关于序列化的部分rdtsc。而且,您不需要为此使用内联汇编;使用__rdtsc()和_mm_lfence(). (但与微基准测试一样,检查编译器的 asm 输出以确保它执行您想要的操作并不是一个坏主意。)
你无法避免开销,与几条指令的成本相比,它总是很大。
还有clflush 通过 C 函数使缓存行无效,作为减去测量开销的示例。
但还要注意,通常将测试代码放入循环中更有用,因为结果准备好之前的执行延迟比等待指令实际从 ROB 中退出更有意义。请参阅NASM 中的 RDTSCP 始终返回相同的值(对单个指令进行计时),以获取测量单个 insn 吞吐量/延迟的示例(在 asm 中)。
| 归档时间: |
|
| 查看次数: |
1298 次 |
| 最近记录: |