RDTSC开销的差异

Joh*_*mew 13 c++ performance assembly intel rdtsc

我正在构建一个微基准来测量性能变化,因为我在一些原始图像处理操作中尝试使用SIMD指令内在函数.但是,编写有用的微基准测试很困难,因此我想首先了解(如果可能的话)消除尽可能多的变异和误差源.

我必须考虑的一个因素是测量代码本身的开销.我正在使用RDTSC进行测量,我正在使用以下代码来查找测量开销:

extern inline unsigned long long __attribute__((always_inline)) rdtsc64() {
    unsigned int hi, lo;
        __asm__ __volatile__(
            "xorl %%eax, %%eax\n\t"
            "cpuid\n\t"
            "rdtsc"
        : "=a"(lo), "=d"(hi)
        : /* no inputs */
        : "rbx", "rcx");
    return ((unsigned long long)hi << 32ull) | (unsigned long long)lo;
}

unsigned int find_rdtsc_overhead() {
    const int trials = 1000000;

    std::vector<unsigned long long> times;
    times.resize(trials, 0.0);

    for (int i = 0; i < trials; ++i) {
        unsigned long long t_begin = rdtsc64();
        unsigned long long t_end = rdtsc64();
        times[i] = (t_end - t_begin);
    }

    // print frequencies of cycle counts
}
Run Code Online (Sandbox Code Playgroud)

运行此代码时,我得到如下输出:

Frequency of occurrence (for 1000000 trials):
234 cycles (counted 28 times)
243 cycles (counted 875703 times)
252 cycles (counted 124194 times)
261 cycles (counted 37 times)
270 cycles (counted 2 times)
693 cycles (counted 1 times)
1611 cycles (counted 1 times)
1665 cycles (counted 1 times)
... (a bunch of larger times each only seen once)
Run Code Online (Sandbox Code Playgroud)

我的问题是这些:

  1. 上述代码生成的循环计数双模分布的可能原因是什么?
  2. 为什么最快的时间(234个周期)只发生几次 - 非常不寻常的情况会减少计数?

更多的信息

平台:

  • Linux 2.6.32(Ubuntu 10.04)
  • g ++ 4.4.3
  • Core 2 Duo(E6600); 这具有恒定速率的TSC.

SpeedStep已关闭(处理器设置为性能模式,运行频率为2.4GHz); 如果在'ondemand'模式下运行,我在243和252个周期得到两个峰值,在360和369个周期得到两个(可能是相应的)峰值.

我正在使用sched_setaffinity将进程锁定到一个核心.如果我依次在每个核心上运行测试(即,锁定到核心0并运行,然后锁定到核心1并运行),我得到两个核心的类似结果,除了234个循环的最快时间往往略有发生核心1的次数少于核心0的次数.

编译命令是:

g++ -Wall -mssse3 -mtune=core2 -O3 -o test.bin test.cpp
Run Code Online (Sandbox Code Playgroud)

GCC为核心循环生成的代码是:

.L105:
#APP
# 27 "test.cpp" 1
    xorl %eax, %eax
    cpuid
    rdtsc
# 0 "" 2
#NO_APP
    movl    %edx, %ebp
    movl    %eax, %edi
#APP
# 27 "test.cpp" 1
    xorl %eax, %eax
    cpuid
    rdtsc
# 0 "" 2
#NO_APP
    salq    $32, %rdx
    salq    $32, %rbp
    mov %eax, %eax
    mov %edi, %edi
    orq %rax, %rdx
    orq %rdi, %rbp
    subq    %rbp, %rdx
    movq    %rdx, (%r8,%rsi)
    addq    $8, %rsi
    cmpq    $8000000, %rsi
    jne .L105
Run Code Online (Sandbox Code Playgroud)

dus*_*uff 7

RDTSC 由于多种原因,可能会返回不一致的结果:

  • 在某些CPU(特别是某些较旧的Opteron)上,TSC在内核之间不同步.听起来你已经通过使用来处理这个sched_setaffinity- 好!
  • 如果在代码运行时OS定时器中断触发,则在运行时会引入延迟.没有切实可行的方法来避免这种情况; 只是扔掉异常高的价值.
  • CPU中的流水线工件有时会在紧密循环中向任何方向抛出几个循环.完全可能有一些在非整数个时钟周期内运行的循环.
  • 缓存!根据CPU缓存的变化,内存操作(如写入times[])的速度可能不同.在这种情况下,您很幸运,std::vector正在使用的实现只是一个扁平数组; 即便如此,这种写作也会让事情发生.这可能是此代码最重要的因素.

我不足以成为Core2微体系结构的大师,说明你为什么要获得这种双峰分布,或者你的代码如何更快地运行28次,但它可能与上述原因之一有关.