7

我正在尝试比较 c++11 测量的时间std::chrono::high_resolution_clockrdtsc_clock下面的时钟。从high_resolution_clock,我得到类似 11000、3000、1000、0 的结果。从rdtsc_clock,我得到 134、15、91 等。为什么它们的结果看起来如此不同?从我的直觉来看,我相信rdtsc_clock正在呈现〜准确的结果,对吗?

    template<std::intmax_t clock_freq>
    struct rdtsc_clock {
        typedef unsigned long long rep;
        typedef std::ratio<1, clock_freq> period;
        typedef std::chrono::duration<rep, period> duration;
        typedef std::chrono::time_point<rdtsc_clock> time_point;
        static const bool is_steady = true;

        static time_point now() noexcept
        {

            unsigned lo, hi;
            asm volatile("rdtsc" : "=a" (lo), "=d" (hi));

            return time_point(duration(static_cast<rep>(hi) << 32 | lo));
        }
    };

计时码:

typedef std::chrono::high_resolution_clock Clock;
//typedef rdtsc_clock<3300000000> Clock;
typedef std::chrono::nanoseconds nanoseconds;
typedef std::chrono::duration<double, typename Clock::period> Cycle;
for(int n=0; n < 10; n++){
   auto t1 = Clock::now();
   //codes
   auto t2 = Clock::now();
   printf(%.0f ns \n", duration_cast<nanoseconds>(Cycle(t2 - t1)).count());
}
4

3 回答 3

8

RDTSC 使用问题

如果您阅读有关 RDTSC 的一些在线文档,您会发现它不能确保 RDTSC 指令之后的指令在 RDTSC 指令本身运行之前不会在管道中执行(也不能确保之前的指令不会在之后运行)。通常的建议是在 RDTSC 之前和/或之后立即使用 CPUID 指令来触发此类“序列点”。显然,这会影响程序性能,并且对于某些类型的测量比其他测量更可取(其中平均吞吐量数据比单个样本更感兴趣)。您可以期望标准库实现对此更加谨慎,这可能是其测量值高得多的原因之一。

跨核心问题(与您的评论无关)

每个 CPU 内核都维护自己的 TSC 寄存器……如果您只是开始在未绑定到内核的线程上或在未绑定到同一内核的多个线程上进行采样,您可能会看到值的“奇怪”跳跃。一些公司(例如微软)坚持认为硬件抽象层(HAL)负责尝试使寄存器尽可能接近同步,但许多(甚至是全新的高端)PC 根本无法做到这一点。

您可以通过绑定到核心来解决这个问题,或者通过执行一些测量跨核心增量的校准步骤(具有一些校准误差范围),然后根据从中采样的核心调整后续样本(这本身就是在大多数 CPU 上很难确定——您需要在 CPUID 指令或类似指令之间旋转采样)。

于 2013-11-01T02:07:05.197 回答
1

我认为你不是在比较同一件事,在我的 Mac 上这个示例工作,rdtsc 和 std::chrono 给出相同数量的周期,如果它可以帮助:

#include <iostream>
#include <vector>
#include <numeric>
#include <chrono>

static __inline__ unsigned long long rdtsc(void){
    unsigned hi, lo;  
    __asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi));
    return ( (unsigned long long)lo)|( ((unsigned long long)hi)<<32   );
}

static int sink = 0;

int main(){
    typedef std::ratio<1, 2400000000> period; // My mac @ 2.4 GHz
    unsigned long long int a,b;
    for (auto size = 1ull; size < 1000000000ull; size *= 100) {
        // record start time
        auto start = std::chrono::high_resolution_clock::now();
        a = rdtsc();
        // do some work
        std::vector<int> v(size, 42);
        sink = std::accumulate(v.begin(), v.end(), 0u); // make sure it's a side effect
        // record end time
        b = rdtsc();
        auto end = std::chrono::high_resolution_clock::now();
        std::chrono::duration<double, period> diff = end-start;
        std::cout << "Time to fill and iterate a vector of "
                  << size << " ints : " << diff.count() << " [cycle]"
                  << ", old style: "<< b-a << " [cycle] \n";
   }
}


Time to fill and iterate a vector of 1 ints : 13965.6 [cycle], old style: 13731 [cycle] 
Time to fill and iterate a vector of 100 ints : 1065.6 [cycle], old style: 969 [cycle] 
Time to fill and iterate a vector of 10000 ints : 68076 [cycle], old style: 67899 [cycle] 
Time to fill and iterate a vector of 1000000 ints : 5.4853e+06 [cycle], old style: 5483487 [cycle] 
Time to fill and iterate a vector of 100000000 ints : 6.57399e+08 [cycle], old style: 657395277 [cycle] 
于 2016-05-19T20:44:21.043 回答
0

我在我自己的 Mac 笔记本电脑上发现与 Timocafe 的示例代码有很大的不同

clang++ 9.1.0 -O3 Time to fill and iterate a vector of 1 ints : 27650.4 [cycle], old style: 35464 [cycle] Time to fill and iterate a vector of 100 ints : 763.2 [cycle], old style: 939 [cycle] Time to fill and iterate a vector of 10000 ints : 90712.8 [cycle], old style: 117181 [cycle] Time to fill and iterate a vector of 1000000 ints : 4.79993e+06 [cycle], old style: 6199891 [cycle] Time to fill and iterate a vector of 100000000 ints : 4.80331e+08 [cycle], old style: 620426953 [cycle]

g++ 5.5 -O3 Time to fill and iterate a vector of 1 ints : 2400 [cycle], old style: 1324 [cycle] Time to fill and iterate a vector of 100 ints : 0 [cycle], old style: 944 [cycle] Time to fill and iterate a vector of 10000 ints : 96000 [cycle], old style: 125444 [cycle] Time to fill and iterate a vector of 1000000 ints : 5.4648e+06 [cycle], old style: 7059362 [cycle] Time to fill and iterate a vector of 100000000 ints : 5.05517e+08 [cycle], old style: 652940006 [cycle]

像0时间这样的事情很麻烦。这表明编译器围绕 high_precision_clock 重新排序。至少使用我们的汇编代码 rdtsc 计时器,我们可以使用 volatile 来获得我们想要的一些行为。如果我将 rdtsc 调用放在 high_precision_clock 调用中,那么我可以得到一个单调时钟,这会告诉我它是我们程序集中的 volatile 保持顺序。此外,time_point这两个编译器的单位和准确性似乎不同。啊。

于 2018-06-19T18:06:15.470 回答