比较rdtsc clock和c ++ 11 std :: chrono :: high_resolution_clock产生的时间测量结果

时间:2013-11-01 01:52:19

标签: c++ c performance-testing

我正在尝试比较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());
}

3 个答案:

答案 0 :(得分:7)

RDTSC使用问题

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

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

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

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

答案 1 :(得分:0)

我认为你没有比较同样的事情,在我的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] 

答案 2 :(得分: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调用中,那么我可以得到单调时钟,这将告诉我保持装配顺序的是组件中的易失性。而且,time_point在两个编译器中似乎具有不同的单位和准确性。嗯。