我正试图用rdtsc代替clock_gettime(CLOCK_REALTIME &ts),以便根据cpu周期而不是服务器时间对代码执行时间进行基准测试。工作台标记代码的执行时间对于软件来说是至关重要的。我尝试在孤立的核心上运行x86_64 3.20GHzubuntu机器上的代码,得到以下数字:
案例1:时钟获取时间: 24纳米秒
void gettime(Timespec &ts) {
clock_gettime(CLOCK_REALTIME, &ts);
}案例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;
}案例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;
}这里的问题是我知道rdtsc是一个非序列化调用,可以由CPU重新排序,另一个选择是rdtscp,它是一个序列化调用,但rdtscp调用后的指令可以在rdtscp调用之前重新排序。使用内存屏障会增加执行时间。
发布于 2019-02-14 14:11:26
您希望lfence;rdtsc启动时钟,rdtscp;lfence停止时钟,因此屏障超出了时间间隔。
(或者有时您希望lfence;rdtsc;lfence启动时钟,以获得额外的可重复性,而代价是增加开销。)
MFENCE是错误的指令;它不能保证序列化指令流(但实际上它在Skylake上使用最新的微码来修复一个错误)。LFENCE序列化指令流,而不等待存储缓冲区为空,仅供ROB使用。这在英特尔总是正确的,但在AMD上只有在光谱仪的缓解功能,使lfence不仅仅是一个诺普。(我猜AMD不会重新排序WC内存中的movntdqa加载,因此lfence作为内存屏障是没有意义的,它只用作防止投机性执行的执行障碍,或者是RDTSC。)
还请参阅Get CPU cycle count?,其中有一个关于序列化rdtsc的部分。但是,您也不需要使用内联的asm;使用__rdtsc()和_mm_lfence()。(但与一般的微基准测试一样,检查编译器的asm输出以确保它实现了您想要的结果也不失为一个好主意。)
您无法避免开销,与一对指令的成本相比,它总是很重要的。
另外,clflush to invalidate cache line via C function作为一个减去测量开销的例子。
但是还要注意的是,通常将代码放在循环中更有用,因为在结果准备好之前执行延迟比等待指令从ROB中实际退出更有意义。请参阅RDTSCP in NASM always returns the same value中的一个例子(在asm中),以度量单个insn的吞吐量/延迟。
https://stackoverflow.com/questions/54690703
复制相似问题