Assembly 使用rdtsc对英特尔进行汇编基准测试给出了奇怪的答案,为什么? 前,我问了一个关于堆栈溢出的问题,并展示了如何在C++中执行RDTCSC操作码。我最近使用rdtsc创建了一个基准函数,如下所示: inline unsigned long long rdtsc() { unsigned int lo, hi; asm volatile ( "cpuid \n" "rdtsc" : "=a"(lo), "=d"(hi) /* outputs */ : "a"(0) /* inputs */ : "%ebx", "%ecx"); /* clobbers*/ return ((unsigned long long)lo) | (((unsigned long long)hi) << 32); } typedef uint64_t (*FuncOneInt)(uint32_t n); /** time a function that takes an integer parameter and returns a 64 bit number Since this is capable of timing in clock cycles, we won't have to do it a huge number of times and divide, we can literally count clocks. Don't forget that everything takes time including getting into and out of the function. You may want to time an empty function. The time to do the computation can be compute by taking the time of the function you want minus the empty one. */ void clockBench(const char* msg, uint32_t n, FuncOneInt f) { uint64_t t0 = rdtsc(); uint64_t r = f(n); uint64_t t1 = rdtsc(); std::cout << msg << "n=" << n << "\telapsed=" << (t1-t0) << '\n'; }

Assembly 使用rdtsc对英特尔进行汇编基准测试给出了奇怪的答案,为什么? 前,我问了一个关于堆栈溢出的问题,并展示了如何在C++中执行RDTCSC操作码。我最近使用rdtsc创建了一个基准函数,如下所示: inline unsigned long long rdtsc() { unsigned int lo, hi; asm volatile ( "cpuid \n" "rdtsc" : "=a"(lo), "=d"(hi) /* outputs */ : "a"(0) /* inputs */ : "%ebx", "%ecx"); /* clobbers*/ return ((unsigned long long)lo) | (((unsigned long long)hi) << 32); } typedef uint64_t (*FuncOneInt)(uint32_t n); /** time a function that takes an integer parameter and returns a 64 bit number Since this is capable of timing in clock cycles, we won't have to do it a huge number of times and divide, we can literally count clocks. Don't forget that everything takes time including getting into and out of the function. You may want to time an empty function. The time to do the computation can be compute by taking the time of the function you want minus the empty one. */ void clockBench(const char* msg, uint32_t n, FuncOneInt f) { uint64_t t0 = rdtsc(); uint64_t r = f(n); uint64_t t1 = rdtsc(); std::cout << msg << "n=" << n << "\telapsed=" << (t1-t0) << '\n'; },assembly,x86,intel,microbenchmark,rdtsc,Assembly,X86,Intel,Microbenchmark,Rdtsc,我可以理解是否由于中断或其他情况而出现错误,但是考虑到这些例程很短,并且n被选择为小,我假设我可以看到实数。但令我惊讶的是,这是连续两次运行的输出 empty n=100 elapsed=438 Sum 1 to n=100 elapsed=887 empty n=100 elapsed=357 Sum 1 to n=100 elapsed=347 空函数始终显示它所做的事情比它应该做的要多 毕竟,进出函数只涉及一些指令。真正的工作是在循环中完成的。不要在意差异是巨大的这一事实。在第二次

我可以理解是否由于中断或其他情况而出现错误,但是考虑到这些例程很短,并且n被选择为小,我假设我可以看到实数。但令我惊讶的是,这是连续两次运行的输出

empty n=100 elapsed=438
Sum 1 to n=100  elapsed=887

empty n=100 elapsed=357
Sum 1 to n=100  elapsed=347
空函数始终显示它所做的事情比它应该做的要多

毕竟,进出函数只涉及一些指令。真正的工作是在循环中完成的。不要在意差异是巨大的这一事实。在第二次运行中,空函数声称占用了357个时钟周期,而求和所占用的时间更少,这是荒谬的

发生了什么事

空函数始终显示它所做的事情比它应该做的要多

在定时间隔内有
cpuid
<根据Agner Fog的测试,英特尔Sandybridge系列CPU上的code>cpuid需要100到250个核心时钟周期(取决于您忽略设置的输入)。()

但你不是在测量核心时钟周期,你是在测量RDTSC参考周期,它可以大大缩短。(例如,我的Skylake i7-6700k在800MHz时空闲,但参考时钟频率为4008 MHz。)有关我尝试在
rdtsc
上获得规范答案的信息,请参阅

首先预热CPU,或者在另一个核心上运行
暂停
忙循环,使其保持最大值(假设它是台式机/笔记本电脑双核或四核,所有核心频率都锁定在一起)


不要在意差异是巨大的这一事实。在第二次运行中,空函数声称占用了357个时钟周期,而求和所占用的时间更少,这是荒谬的

这种效应是否也一致

也许在打印第三行消息期间/之后,您的CPU加速到全速,使最后一个计时区域运行得更快?()

IDK在
cpuid
之前,eax和ecx中的不同垃圾会有多大影响。将其替换为
lfence
,以消除这种情况,并使用开销更低的方式序列化
rdtsc

空函数始终显示它所做的事情比它应该做的要多

在定时间隔内有
cpuid
<根据Agner Fog的测试,英特尔Sandybridge系列CPU上的code>cpuid需要100到250个核心时钟周期(取决于您忽略设置的输入)。()

但你不是在测量核心时钟周期,你是在测量RDTSC参考周期,它可以大大缩短。(例如,我的Skylake i7-6700k在800MHz时空闲,但参考时钟频率为4008 MHz。)有关我尝试在
rdtsc
上获得规范答案的信息,请参阅

首先预热CPU,或者在另一个核心上运行
暂停
忙循环,使其保持最大值(假设它是台式机/笔记本电脑双核或四核,所有核心频率都锁定在一起)


不要在意差异是巨大的这一事实。在第二次运行中,空函数声称占用了357个时钟周期,而求和所占用的时间更少,这是荒谬的

这种效应是否也一致

也许在打印第三行消息期间/之后,您的CPU加速到全速,使最后一个计时区域运行得更快?()


IDK在
cpuid
之前,eax和ecx中的不同垃圾会有多大影响。将其替换为
lfence
,以消除这一点,并使用低得多的开销方式序列化
rdtsc

您使用了哪些编译器选项?对于gcc和/或clang,循环应在启用优化的情况下优化为乘法(高斯公式)。您是否只对第一次调用此函数进行计时,其中可能存在I-cache未命中?它可能和求和函数在同一行。此外,在
rdtsc
之后,您不会执行任何类似于
lfence
的操作来在时钟开始之前停止“工作”的运行。请参见示例。是否控制涡轮频率
rdtsc
不计时核心时钟周期,只是在现代CPU上的墙上时钟等效参考周期。我不知道lfence,也不知道用高斯公式控制时钟的叮当声。但是,
gcc-O3
没有发现它,只是自动矢量化了添加的内容。在测量小代码序列时,可能会出现很多问题,我强烈建议使用现有的基准框架,如Google benchmark。您的目标是准确地度量此方法,还是了解更多关于编写可靠基准而不使用第三方组件的信息?您使用了哪些编译器选项?对于gcc和/或clang,循环应在启用优化的情况下优化为乘法(高斯公式)。您是否只对第一次调用此函数进行计时,其中可能存在I-cache未命中?它可能和求和函数在同一行。此外,在
rdtsc
之后,您不会执行任何类似于
lfence
的操作来在时钟开始之前停止“工作”的运行。请参见示例。是否控制涡轮频率
rdtsc
不计时核心时钟周期,只是在现代CPU上的墙上时钟等效参考周期。我不知道lfence,也不知道用高斯公式控制时钟的叮当声。但是,
gcc-O3
没有发现它,只是自动矢量化了添加的内容。在测量小代码序列时,可能会出现很多问题,我强烈建议使用现有的基准框架,如Google benchmark。您的目标是准确地度量这种方法,还是了解更多关于编写可靠基准而不使用第三方组件的信息?lfence和warming工作得非常好。我
g++ -g -O2
empty n=100 elapsed=438
Sum 1 to n=100  elapsed=887

empty n=100 elapsed=357
Sum 1 to n=100  elapsed=347