我试图使用__rdtscp的trying函数来度量时间间隔。目标平台是Linux x64,CPU Intel Xeon X5550。虽然为这个处理器设置了constant_tsc标志,但是校准__rdtscp会得到非常不同的结果:
$ taskset -c 1 ./ticks
Ticks per usec: 256
$ taskset -c 1 ./ticks
Ticks per usec: 330.667
$ taskset -c 1 ./ticks
Ticks per usec: 345.043
$ taskset -c 1 ./ticks
Ticks per usec: 166.054
$ taskset -c 1 ./ticks
Ticks per usec: 256
$ taskset -c 1 ./ticks
Ticks per usec: 345.043
$ taskset -c 1 ./ticks
Ticks per usec: 256
$ taskset -c 1 ./ticks
Ticks per usec: 330.667
$ taskset -c 1 ./ticks
Ticks per usec: 256
$ taskset -c 1 ./ticks
Ticks per usec: 330.667
$ taskset -c 1 ./ticks
Ticks per usec: 330.667
$ taskset -c 1 ./ticks
Ticks per usec: 345.043
$ taskset -c 1 ./ticks
Ticks per usec: 256
$ taskset -c 1 ./ticks
Ticks per usec: 125.388
$ taskset -c 1 ./ticks
Ticks per usec: 360.727
$ taskset -c 1 ./ticks
Ticks per usec: 345.043正如我们所看到的,程序执行之间的差异可以达到3倍(125-360)。这种不稳定性不适合任何测量。
下面是代码(gcc 4.9.3,运行在Oracle Linux 6.6上,内核3.8.13-55.1.2.el6uek.x86_64):
// g++ -O3 -std=c++11 -Wall ticks.cpp -o ticks
#include <x86intrin.h>
#include <ctime>
#include <cstdint>
#include <iostream>
int main()
{
timespec start, end;
uint64_t s = 0;
const double rdtsc_ticks_per_usec = [&]()
{
unsigned int dummy;
clock_gettime(CLOCK_MONOTONIC, &start);
uint64_t rd_start = __rdtscp(&dummy);
for (size_t i = 0; i < 1000000; ++i) ++s;
uint64_t rd_end = __rdtscp(&dummy);
clock_gettime(CLOCK_MONOTONIC, &end);
double usec_dur = double(end.tv_sec) * 1E6 + end.tv_nsec / 1E3;
usec_dur -= double(start.tv_sec) * 1E6 + start.tv_nsec / 1E3;
return (double)(rd_end - rd_start) / usec_dur;
}();
std::cout << s << std::endl;
std::cout << "Ticks per usec: " << rdtsc_ticks_per_usec << std::endl;
return 0;
}当我在Windows7,i7-4470,VS2015下运行非常类似的程序时,校准结果是相当稳定的,仅在最后一个数字上相差很小。
所以问题是-那是什么问题?是CPU问题、Linux问题还是我的代码问题?
发布于 2016-03-19 12:19:01
这绝对是我的代码(或gcc)的问题。编译器优化出了用s = 1000000替换它的循环。
为了防止gcc优化这一校准回路,应改变如下:
for (size_t i = 0; i < 1000000; ++i) s += i;或者更简单和正确的方式(感谢哈尔):
for (volatile size_t i = 0; i < 1000000; ++i);发布于 2016-03-19 12:41:07
如果您也不确保cpu是孤立的,其他的抖动源就会出现。您确实希望避免将另一个进程安排在该核心上。此外,理想情况下,您运行的是一个没有痒的内核,这样您就永远不会在内核上运行内核代码。在上面的代码中,我想只有当你不走运的时候,才能在调用clock_gettime()和__rdtscp之间切换到滴答或上下文。
使s易失性是另一种克服这种编译器优化的方法。
https://stackoverflow.com/questions/36101311
复制相似问题