2

我正在尝试使用__rdtscpintrinsinc 函数来测量时间间隔。目标平台是 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;
}

当我在 Windows 7、i7-4470、VS2015 下运行非常相似的程序时,校准结果相当稳定,只有最后一位的差异很小。

所以问题是——这个问题是关于什么的?是 CPU 问题、Linux 问题还是我的代码问题?

4

2 回答 2

3

如果您不确保 cpu 是隔离的,那么还会存在其他抖动源。您确实希望避免在该核心上安排另一个进程。同样理想的是,您运行一个无滴答内核,这样您就永远不会在该内核上运行内核代码。在上面的代码中,我猜只有当你不幸在 clock_gettime() 和 __rdtscp 调用之间获得滴答声或上下文切换时才有意义

使 s 易变是另一种击败这种编译器优化的方法。

于 2016-03-19T12:41:07.960 回答
2

绝对是我的代码(或 gcc)问题。编译器优化了循环,将其替换为s = 1000000.

为了防止 gcc 优化这个校准循环,应该这样改变:

for (size_t i = 0; i < 1000000; ++i) s += i;

或者更简单正确的方式(感谢 Hal):

for (volatile size_t i = 0; i < 1000000; ++i);
于 2016-03-19T12:19:01.190 回答