使用C+提供以纳米秒为单位的时间的计时器功能

使用C+提供以纳米秒为单位的时间的计时器功能

我希望计算API返回值所需的时间。这种行动所需的时间是在纳米秒的空间内。由于API是一个C+类/函数,所以我使用timer.h来计算相同的内容:

  #include <ctime>
  #include <cstdio>

  using namespace std;

  int main(int argc, char** argv) {

      clock_t start;
      double diff;
      start = clock();
      diff = ( std::clock() - start ) / (double)CLOCKS_PER_SEC;
      cout<<"printf: "<< diff <<'\n';

      return 0;
  }

以上代码以秒为单位给出了时间。如何在纳米秒内以更高的精度获得相同的结果?


饮歌长啸
浏览 741回答 3
3回答

慕尼黑5688855

这个新的答案使用C+11的。<chrono>设施。虽然还有其他的答案可以说明如何使用<chrono>,它们都没有显示如何使用<chrono>带着RDTSC其他几个答案中提到的设施。所以我想我应该展示如何使用RDTSC带着<chrono>..此外,我将演示如何在时钟上临时编写测试代码,以便您可以在RDTSC和你的系统内置的时钟设施(这很可能是基于clock(),&nbsp;clock_gettime()和/或QueryPerformanceCounter.注意,RDTSC指令是x86特定的。QueryPerformanceCounter只适用于Windows。和clock_gettime()仅限POSIX。下面我介绍两个新的时钟:std::chrono::high_resolution_clock和std::chrono::system_clock,如果您可以假设C+11,则现在是跨平台的。首先,下面是如何从Intel中创建一个与C+11兼容的时钟。rdtsc装配指令。我就叫它x::clock:#include&nbsp;<chrono>namespace&nbsp;x{struct&nbsp;clock{ &nbsp;&nbsp;&nbsp;&nbsp;typedef&nbsp;unsigned&nbsp;long&nbsp;long&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rep; &nbsp;&nbsp;&nbsp;&nbsp;typedef&nbsp;std::ratio<1,&nbsp;2'800'000'000>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;period;&nbsp;//&nbsp;My&nbsp;machine&nbsp;is&nbsp;2.8&nbsp;GHz &nbsp;&nbsp;&nbsp;&nbsp;typedef&nbsp;std::chrono::duration<rep,&nbsp;period>&nbsp;duration; &nbsp;&nbsp;&nbsp;&nbsp;typedef&nbsp;std::chrono::time_point<clock>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;time_point; &nbsp;&nbsp;&nbsp;&nbsp;static&nbsp;const&nbsp;bool&nbsp;is_steady&nbsp;=&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;true; &nbsp;&nbsp;&nbsp;&nbsp;static&nbsp;time_point&nbsp;now()&nbsp;noexcept &nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;unsigned&nbsp;lo,&nbsp;hi; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;asm&nbsp;volatile("rdtsc"&nbsp;:&nbsp;"=a"&nbsp;(lo),&nbsp;"=d"&nbsp;(hi)); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;time_point(duration(static_cast<rep>(hi)&nbsp;<<&nbsp;32&nbsp;|&nbsp;lo)); &nbsp;&nbsp;&nbsp;&nbsp;} }; }&nbsp;&nbsp;//&nbsp;x这个时钟所做的就是计算CPU周期,并将其存储在一个无符号64位整数中。您可能需要调整编译器的汇编语言语法。或者您的编译器可能提供一个您可以使用的内部特性(例如:now() {return __rdtsc();}).要构建一个时钟,您必须给它表示(存储类型)。您还必须提供时钟周期,这必须是一个编译时间常数,即使您的机器可能改变不同的电源模式下的时钟速度。根据这些基本原理,您可以很容易地定义您的时钟的“本地”时间持续时间和时间点。如果你想要做的只是输出时钟滴答的数量,那么你给出的时钟周期是多少并不重要。只有当您想将时钟滴答的数量转换为一些实时单位(例如纳秒)时,这个常数才会起作用。在这种情况下,你能提供的时钟速度越精确,转换到纳秒(毫秒,随便什么)的精度就越高。下面是示例代码,演示如何使用x::clock..实际上,我已经对时钟上的代码进行了模板化,因为我想向您展示如何用完全相同的语法使用许多不同的时钟。这个特殊的测试显示了在循环下运行所需时间时的循环开销是什么:#include&nbsp;<iostream>template&nbsp;<class&nbsp;clock>voidtest_empty_loop(){ &nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;Define&nbsp;real&nbsp;time&nbsp;units &nbsp;&nbsp;&nbsp;&nbsp;typedef&nbsp;std::chrono::duration<unsigned&nbsp;long&nbsp;long,&nbsp;std::pico>&nbsp;picoseconds; &nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;or: &nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;typedef&nbsp;std::chrono::nanoseconds&nbsp;nanoseconds; &nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;Define&nbsp;double-based&nbsp;unit&nbsp;of&nbsp;clock&nbsp;tick &nbsp;&nbsp;&nbsp;&nbsp;typedef&nbsp;std::chrono::duration<double,&nbsp;typename&nbsp;clock::period>&nbsp;Cycle; &nbsp;&nbsp;&nbsp;&nbsp;using&nbsp;std::chrono::duration_cast; &nbsp;&nbsp;&nbsp;&nbsp;const&nbsp;int&nbsp;N&nbsp;=&nbsp;100000000; &nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;Do&nbsp;it &nbsp;&nbsp;&nbsp;&nbsp;auto&nbsp;t0&nbsp;=&nbsp;clock::now(); &nbsp;&nbsp;&nbsp;&nbsp;for&nbsp;(int&nbsp;j&nbsp;=&nbsp;0;&nbsp;j&nbsp;<&nbsp;N;&nbsp;++j) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;asm&nbsp;volatile(""); &nbsp;&nbsp;&nbsp;&nbsp;auto&nbsp;t1&nbsp;=&nbsp;clock::now(); &nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;Get&nbsp;the&nbsp;clock&nbsp;ticks&nbsp;per&nbsp;iteration &nbsp;&nbsp;&nbsp;&nbsp;auto&nbsp;ticks_per_iter&nbsp;=&nbsp;Cycle(t1-t0)/N; &nbsp;&nbsp;&nbsp;&nbsp;std::cout&nbsp;<<&nbsp;ticks_per_iter.count()&nbsp;<<&nbsp;"&nbsp;clock&nbsp;ticks&nbsp;per&nbsp;iteration\n"; &nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;Convert&nbsp;to&nbsp;real&nbsp;time&nbsp;units &nbsp;&nbsp;&nbsp;&nbsp;std::cout&nbsp;<<&nbsp;duration_cast<picoseconds>(ticks_per_iter).count() &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<<&nbsp;"ps&nbsp;per&nbsp;iteration\n";}这段代码所做的第一件事就是创建一个“实时”单元来显示结果。我选择了皮秒,但是你可以选择任何你喜欢的单位,无论是积分还是基于浮点。举个例子,有一个预先制作的std::chrono::nanoseconds我可以用的单位。作为另一个例子,我希望以浮点数的形式打印出每次迭代的平均时钟周期数,因此我创建了另一个基于Double的持续时间,它具有与时钟的刻度相同的单位(称为Cycle在代码中)。循环是通过调用clock::now()两边都有。如果要命名此函数返回的类型,则如下所示:typename&nbsp;clock::time_point&nbsp;t0&nbsp;=&nbsp;clock::now();(如x::clock例如,也适用于系统提供的时钟)。要获得以浮点时钟为单位的持续时间,只需减去两个时间点,而要获得每一个迭代值,则将持续时间除以迭代次数。属性可以在任何时间内获取计数。count()成员函数这将返回内部表示。最后我用std::chrono::duration_cast若要转换持续时间,请执行以下操作Cycle持续时间picoseconds打印出来。使用此代码很简单:int&nbsp;main(){ &nbsp;&nbsp;&nbsp;&nbsp;std::cout&nbsp;<<&nbsp;"\nUsing&nbsp;rdtsc:\n"; &nbsp;&nbsp;&nbsp;&nbsp;test_empty_loop<x::clock>(); &nbsp;&nbsp;&nbsp;&nbsp;std::cout&nbsp;<<&nbsp;"\nUsing&nbsp;std::chrono::high_resolution_clock:\n"; &nbsp;&nbsp;&nbsp;&nbsp;test_empty_loop<std::chrono::high_resolution_clock>(); &nbsp;&nbsp;&nbsp;&nbsp;std::cout&nbsp;<<&nbsp;"\nUsing&nbsp;std::chrono::system_clock:\n"; &nbsp;&nbsp;&nbsp;&nbsp;test_empty_loop<std::chrono::system_clock>();}以上我是用我们自制的测试来练习的。x::clock,并将这些结果与使用两个系统提供的时钟的结果进行比较:std::chrono::high_resolution_clock和std::chrono::system_clock..对我来说这是打印出来的:Using&nbsp;rdtsc: 1.72632&nbsp;clock&nbsp;ticks&nbsp;per&nbsp;iteration 616ps&nbsp;per&nbsp;iteration Using&nbsp;std::chrono::high_resolution_clock: 0.620105&nbsp;clock&nbsp;ticks&nbsp;per&nbsp;iteration 620ps&nbsp;per&nbsp;iteration Using&nbsp;std::chrono::system_clock: 0.00062457&nbsp;clock&nbsp;ticks&nbsp;per&nbsp;iteration 624ps&nbsp;per&nbsp;iteration这表明每个时钟都有一个不同的刻度周期,因为每个时钟的每一次迭代的滴答数都有很大的不同。然而,当转换为一个已知的时间单位(例如,皮秒),我得到了大约相同的结果,每个时钟(您的里程可能有所不同)。请注意,我的代码完全没有“神奇的转换常量”。实际上,整个示例中只有两个神奇的数字:我的机器的时钟速度来定义x::clock.要测试的迭代次数。如果更改这个数目会使结果有很大差异,那么您可能应该提高迭代次数,或者在测试时清空计算机上的竞争进程。

慕沐林林

有了这样的精确度,最好是在cpu滴答中进行推理,而不是在系统调用中进行推理。像钟()..别忘了,如果执行一条指令需要超过一纳秒.拥有纳秒精度几乎是不可能的。不过,差不多吧是一个开始:以下是检索自CPU上次启动以来传递的80x86 CPU时钟滴答号的实际代码。它将在奔腾及以上领域开展工作(386/486没有得到支持)。这段代码实际上是特定于MSVisualC+的,但是只要它支持内联程序集,它可能很容易移植到其他任何东西。inline&nbsp;__int64&nbsp;GetCpuClocks(){ &nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;Counter &nbsp;&nbsp;&nbsp;&nbsp;struct&nbsp;{&nbsp;int32&nbsp;low,&nbsp;high;&nbsp;}&nbsp;counter; &nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;Use&nbsp;RDTSC&nbsp;instruction&nbsp;to&nbsp;get&nbsp;clocks&nbsp;count &nbsp;&nbsp;&nbsp;&nbsp;__asm&nbsp;push&nbsp;EAX &nbsp;&nbsp;&nbsp;&nbsp;__asm&nbsp;push&nbsp;EDX &nbsp;&nbsp;&nbsp;&nbsp;__asm&nbsp;__emit&nbsp;0fh&nbsp;__asm&nbsp;__emit&nbsp;031h&nbsp;//&nbsp;RDTSC &nbsp;&nbsp;&nbsp;&nbsp;__asm&nbsp;mov&nbsp;counter.low,&nbsp;EAX &nbsp;&nbsp;&nbsp;&nbsp;__asm&nbsp;mov&nbsp;counter.high,&nbsp;EDX &nbsp;&nbsp;&nbsp;&nbsp;__asm&nbsp;pop&nbsp;EDX &nbsp;&nbsp;&nbsp;&nbsp;__asm&nbsp;pop&nbsp;EAX&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;Return&nbsp;result &nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;*(__int64&nbsp;*)(&counter);}这个函数还具有非常快的优点-它通常不需要超过50个CPU周期来执行。使用计时图:如果您需要将时钟计数转换为真正经过的时间,请将结果除以芯片的时钟速度。记住,“额定”GHz很可能与你的芯片的实际速度略有不同。要检查芯片的真实速度,可以使用几个非常好的实用程序或Win 32调用QueryPerformanceFrequy()。
打开App,查看更多内容
随时随地看视频慕课网APP