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

我试图比较c ++ 11 std::chrono::high_resolution_clock rdtsc_clock std::chrono::high_resolution_clock rdtsc_clock时间和下面的rdtsc_clock时钟。 从high_resolution_clock ,我得到的结果如rdtsc_clock 。从rdtsc_clock ,我得到rdtsc_clock等。为什么他们的结果看起来如此不同? 从我的直觉来看,我相信rdtsc_clock正在呈现〜准确的结果,对吗?

  template struct rdtsc_clock { typedef unsigned long long rep; typedef std::ratio period; typedef std::chrono::duration duration; typedef std::chrono::time_point 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(hi) << 32 | lo)); } }; 

时间码:

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

RDTSC使用问题

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

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

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

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

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

 #include  #include  #include  #include  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 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 diff = end-start; std::cout << "Time to fill and iterate a vector of " << size << " ints : " << diff.count() << " [cycle]" << ", old style: "<< ba << " [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] 

我发现Timocafe在我自己的Mac笔记本电脑上的示例代码存在很大差异

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似乎在两个编译器中具有不同的单位和准确度。 啊。