我正在尝试比较c ++ 11 std::chrono::high_resolution_clock
和rdtsc_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());
}
答案 0 :(得分:7)
如果您在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
在两个编译器中似乎具有不同的单位和准确性。嗯。