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

gag*_*eet 101 c c++ timer

我希望计算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;
  }
Run Code Online (Sandbox Code Playgroud)

上面的代码给出了以秒为单位的时间.如何在毫秒秒内获得相同的精度?

gri*_*eve 83

其他人发布的关于在循环中重复运行该函数的内容是正确的.

对于Linux(和BSD),您希望使用clock_gettime().

#include <sys/time.h>

int main()
{
   timespec ts;
   // clock_gettime(CLOCK_MONOTONIC, &ts); // Works on FreeBSD
   clock_gettime(CLOCK_REALTIME, &ts); // Works on Linux
}
Run Code Online (Sandbox Code Playgroud)

对于Windows,您要使用QueryPerformanceCounter.QPC上有更多内容

显然在某些芯片组上存在QPC 的已知问题,因此您可能希望确保没有这些芯片组.此外,一些双核AMD也可能导致问题.请参阅sebbbi的第二篇文章,其中他说:

QueryPerformanceCounter()和QueryPerformanceFrequency()提供了更好的分辨率,但有不同的问题.例如在Windows XP中,所有AMD Athlon X2双核CPU都会"随机"返回任一核心的PC(PC有时会向后跳跃),除非您专门安装AMD双核驱动程序包来解决问题.我们还没有注意到任何其他具有类似问题的双+核CPU(p4 dual,p4 ht,core2 dual,core2 quad,phenom quad).

编辑2013/07/16:

http://msdn.microsoft.com/en-us/library/windows/desktop/ee417693(v=vs.85).aspx中所述的某些情况下,QPC的功效似乎存在争议.

...当QueryPerformanceCounter和QueryPerformanceFrequency通常针对多个处理器进行调整时,BIOS或驱动程序中的错误可能会导致这些例程在线程从一个处理器移动到另一个处理器时返回不同的值...

但是,这个StackOverflow回答/sf/answers/321202381/指出QPC应该在Win XP Service Pack 2之后的任何MS OS上正常工作.

本文显示Windows 7可以确定处理器是否具有不变的TSC,如果不是,则可以回退到外部计时器.http://performancebydesign.blogspot.com/2012/03/high-resolution-clocks-and-timers-for.html跨处理器同步仍是一个问题.

与计时器相关的其他精细阅读:

有关详细信息,请参阅注释.

  • CLOCK_MONOTONIC适用于我可以使用的Linux版本. (6认同)
  • 实际上,如果可用的话,你必须使用`CLOCK_MONOTONIC_RAW`,以便不通过NTP调整硬件时间. (2认同)

How*_*ant 68

这个新答案使用C++ 11的<chrono>工具.虽然还有其他答案显示如何使用<chrono>,但没有一个显示如何使用此处其他几个答案中提到<chrono>RDTSC设施.所以我想我会展示如何使用RDTSC<chrono>.此外,我将演示如何模板化时钟上的测试代码,这样就可以快速切换RDTSC和系统的内置时钟设施(这将可能是基于clock(),clock_gettime()和/或QueryPerformanceCounter.

请注意,该RDTSC指令是特定于x86的. QueryPerformanceCounter仅限Windows.并且clock_gettime()只是POSIX.下面我介绍两个新的时钟:std::chrono::high_resolution_clock并且std::chrono::system_clock,如果你可以假设C++ 11,现在是跨平台的.

首先,以下是如何使用英特尔rdtsc汇编指令创建C++ 11兼容时钟.我会称之为x::clock:

#include <chrono>

namespace x
{

struct clock
{
    typedef unsigned long long                 rep;
    typedef std::ratio<1, 2'800'000'000>       period; // My machine is 2.8 GHz
    typedef std::chrono::duration<rep, period> duration;
    typedef std::chrono::time_point<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));
    }
};

}  // x
Run Code Online (Sandbox Code Playgroud)

所有这些时钟都计算CPU周期并将其存储在无符号的64位整数中.您可能需要调整编译器的汇编语言语法.或者您的编译器可能会提供您可以使用的内在函数(例如now() {return __rdtsc();}).

要构建时钟,您必须为其提供表示(存储类型).您还必须提供时钟周期,该周期必须是编译时间常数,即使您的机器可能会在不同的功耗模式下更改时钟速度.从这些基础知识中,您可以轻松地定义时钟的"原生"持续时间和时间点.

如果您只想输出时钟周期数,那么您在时钟周期内给出的数字并不重要.如果您想将时钟周期数转换为某些实时单位(如纳秒),则此常量才会起作用.在这种情况下,您能够提供更准确的时钟速度,转换为纳秒(毫秒,无论如何)的准确度就越高.

下面是显示如何使用的示例代码x::clock.实际上我已经模仿了时钟上的代码,因为我想展示如何使用完全相同的语法来使用许多不同的时钟.此特定测试显示了在循环下运行您想要的时间时循环开销:

#include <iostream>

template <class clock>
void
test_empty_loop()
{
    // Define real time units
    typedef std::chrono::duration<unsigned long long, std::pico> picoseconds;
    // or:
    // typedef std::chrono::nanoseconds nanoseconds;
    // Define double-based unit of clock tick
    typedef std::chrono::duration<double, typename clock::period> Cycle;
    using std::chrono::duration_cast;
    const int N = 100000000;
    // Do it
    auto t0 = clock::now();
    for (int j = 0; j < N; ++j)
        asm volatile("");
    auto t1 = clock::now();
    // Get the clock ticks per iteration
    auto ticks_per_iter = Cycle(t1-t0)/N;
    std::cout << ticks_per_iter.count() << " clock ticks per iteration\n";
    // Convert to real time units
    std::cout << duration_cast<picoseconds>(ticks_per_iter).count()
              << "ps per iteration\n";
}
Run Code Online (Sandbox Code Playgroud)

这段代码所做的第一件事是创建一个"实时"单元来显示结果.我选择了皮秒,但你可以选择你喜欢的任何单位,无论是积分还是基于浮点.作为一个例子,std::chrono::nanoseconds我可以使用一个预制单元.

作为另一个例子,我想打印出每次迭代的平均时钟周期数作为一个浮点,所以我创建了另一个基于double的持续时间,它具有与时钟嘀嗒相同的单位(Cycle在代码中调用).

循环定时调用clock::now()任何一方.如果要命名从此函数返回的类型,则为:

typename clock::time_point t0 = clock::now();
Run Code Online (Sandbox Code Playgroud)

(如x::clock示例中清楚所示,系统提供的时钟也是如此).

为了获得浮点时钟标记的持续时间,只需减去两个时间点,并获得每次迭代值,将该持续时间除以迭代次数.

您可以使用count()成员函数在任何持续时间内获取计数.这将返回内部表示.最后我std::chrono::duration_cast用来将持续时间转换为持续Cycle时间picoseconds并打印出来.

要使用此代码很简单:

int main()
{
    std::cout << "\nUsing rdtsc:\n";
    test_empty_loop<x::clock>();

    std::cout << "\nUsing std::chrono::high_resolution_clock:\n";
    test_empty_loop<std::chrono::high_resolution_clock>();

    std::cout << "\nUsing std::chrono::system_clock:\n";
    test_empty_loop<std::chrono::system_clock>();
}
Run Code Online (Sandbox Code Playgroud)

上面我使用我们自制的测试进行测试x::clock,并使用两个系统提供的时钟比较这些结果: std::chrono::high_resolution_clockstd::chrono::system_clock.对我来说这打印出来:

Using rdtsc:
1.72632 clock ticks per iteration
616ps per iteration

Using std::chrono::high_resolution_clock:
0.620105 clock ticks per iteration
620ps per iteration

Using std::chrono::system_clock:
0.00062457 clock ticks per iteration
624ps per iteration
Run Code Online (Sandbox Code Playgroud)

这表明这些时钟中的每一个具有不同的滴答周期,因为每个时钟的每次迭代的滴答是非常不同的.但是,当转换为已知的时间单位(例如皮秒)时,每个时钟的结果大致相同(您的里程可能会有所不同).

请注意我的代码完全没有"魔术转换常量".实际上,整个例子中只有两个幻数:

  1. 我的机器的时钟速度,以便定义x::clock.
  2. 要测试的迭代次数.如果更改此数字会使您的结果差异很大,那么您应该更多地进行迭代次数,或者在测试时清空计算机中的竞争进程.

  • 通过"RDTSC仅限英特尔",你真的指的是x86架构和衍生产品,不是吗?[AMD,Cyrix,Transmeta x86芯片都有说明](http://stackoverflow.com/a/8605960/103167),而英特尔RISC和ARM处理器则没有. (5认同)
  • +1.就C++ 11中的测量而言,这是唯一的好答案. (4认同)
  • @BenVoigt:+1 是的,您的更正非常正确,谢谢。 (2认同)

Von*_*onC 27

有了这样的准确度,最好在CPU tick中推理,而不是像clock()那样在系统调用中推理.并且不要忘记,如果执行指令需要超过一纳秒......具有纳秒精度几乎是不可能的.

不过,这样的事情还是一个开始:

这是检索自上次启动CPU以来传递的80x86 CPU时钟周期数的实际代码.它适用于奔腾及以上版本(不支持386/486).这段代码实际上是MS Visual C++特有的,但可以很容易地移植到其他任何东西,只要它支持内联汇编.

inline __int64 GetCpuClocks()
{

    // Counter
    struct { int32 low, high; } counter;

    // Use RDTSC instruction to get clocks count
    __asm push EAX
    __asm push EDX
    __asm __emit 0fh __asm __emit 031h // RDTSC
    __asm mov counter.low, EAX
    __asm mov counter.high, EDX
    __asm pop EDX
    __asm pop EAX

    // Return result
    return *(__int64 *)(&counter);

}
Run Code Online (Sandbox Code Playgroud)

此功能还具有极快的优点 - 执行通常不超过50个CPU周期.

使用时序图:
如果需要将时钟计数转换为真实的经过时间,请将结果除以芯片的时钟速度.请记住,"额定"GHz可能与芯片的实际速度略有不同.要检查芯片的真实速度,可以使用几个非常好的实用程序或Win32调用QueryPerformanceFrequency().

  • 使用QueryPerformanceFrequency()将TSC计数转换为已用时间可能不起作用.QueryPerformanceCounter()在Vista上使用HPET(高精度事件计时器).如果用户将/ USEPMTIMER添加到boot.ini,它将使用ACPI电源管理计时器. (4认同)

Mar*_*ius 23

要正确执行此操作,您可以使用以下两种方法之一:使用RDTSC或使用clock_gettime().第二个是快2倍,并具有给予正确的绝对时间的优势.请注意,为了RDTSC正常工作,您需要按指示使用它(此页面上的其他注释有错误,并且可能在某些处理器上产生不正确的计时值)

inline uint64_t rdtsc()
{
    uint32_t lo, hi;
    __asm__ __volatile__ (
      "xorl %%eax, %%eax\n"
      "cpuid\n"
      "rdtsc\n"
      : "=a" (lo), "=d" (hi)
      :
      : "%ebx", "%ecx" );
    return (uint64_t)hi << 32 | lo;
}
Run Code Online (Sandbox Code Playgroud)

并且对于clock_gettime :(我任意选择微秒分辨率)

#include <time.h>
#include <sys/timeb.h>
// needs -lrt (real-time lib)
// 1970-01-01 epoch UTC time, 1 mcs resolution (divide by 1M to get time_t)
uint64_t ClockGetTime()
{
    timespec ts;
    clock_gettime(CLOCK_REALTIME, &ts);
    return (uint64_t)ts.tv_sec * 1000000LL + (uint64_t)ts.tv_nsec / 1000LL;
}
Run Code Online (Sandbox Code Playgroud)

产生的时间和价值:

Absolute values:
rdtsc           = 4571567254267600
clock_gettime   = 1278605535506855

Processing time: (10000000 runs)
rdtsc           = 2292547353
clock_gettime   = 1031119636
Run Code Online (Sandbox Code Playgroud)


gag*_*eet 22

我使用以下内容来获得所需的结果:

#include <time.h>
#include <iostream>
using namespace std;

int main (int argc, char** argv)
{
    // reset the clock
    timespec tS;
    tS.tv_sec = 0;
    tS.tv_nsec = 0;
    clock_settime(CLOCK_PROCESS_CPUTIME_ID, &tS);
    ...
    ... <code to check for the time to be put here>
    ...
    clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &tS);
    cout << "Time taken is: " << tS.tv_sec << " " << tS.tv_nsec << endl;

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

  • 我贬低了因为尝试应用此代码我不得不首先谷歌为什么没有定义timespec.然后我不得不谷歌什么POSIX ...所以据我所知,这个代码与那些坚持使用标准库的Windows用户无关. (2认同)

gon*_*aao 8

对于C++ 11,这是一个简单的包装器:

#include <iostream>
#include <chrono>

class Timer
{
public:
    Timer() : beg_(clock_::now()) {}
    void reset() { beg_ = clock_::now(); }
    double elapsed() const {
        return std::chrono::duration_cast<second_>
            (clock_::now() - beg_).count(); }

private:
    typedef std::chrono::high_resolution_clock clock_;
    typedef std::chrono::duration<double, std::ratio<1> > second_;
    std::chrono::time_point<clock_> beg_;
};
Run Code Online (Sandbox Code Playgroud)

或者对于*nix上的C++ 03,

class Timer
{
public:
    Timer() { clock_gettime(CLOCK_REALTIME, &beg_); }

    double elapsed() {
        clock_gettime(CLOCK_REALTIME, &end_);
        return end_.tv_sec - beg_.tv_sec +
            (end_.tv_nsec - beg_.tv_nsec) / 1000000000.;
    }

    void reset() { clock_gettime(CLOCK_REALTIME, &beg_); }

private:
    timespec beg_, end_;
};
Run Code Online (Sandbox Code Playgroud)

用法示例:

int main()
{
    Timer tmr;
    double t = tmr.elapsed();
    std::cout << t << std::endl;

    tmr.reset();
    t = tmr.elapsed();
    std::cout << t << std::endl;
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

来自https://gist.github.com/gongzhitaao/7062087


Gre*_*ill 5

一般来说,为了计算调用函数需要多长时间,你想要做的次数比不止一次多.如果你只调用一次函数并且运行时间很短,你仍然需要实际调用定时器函数的开销,而你不知道需要多长时间.

例如,如果您估计您的函数可能需要800 ns才能运行,请循环调用它一千万次(这将花费大约8秒).将总时间除以一千万来获得每次通话的时间.


Wal*_*ght 5

您可以在x86处理器下运行gcc时使用以下函数:

unsigned long long rdtsc()
{
  #define rdtsc(low, high) \
         __asm__ __volatile__("rdtsc" : "=a" (low), "=d" (high))

  unsigned int low, high;
  rdtsc(low, high);
  return ((ulonglong)high << 32) | low;
}
Run Code Online (Sandbox Code Playgroud)

使用Digital Mars C++:

unsigned long long rdtsc()
{
   _asm
   {
        rdtsc
   }
}
Run Code Online (Sandbox Code Playgroud)

它读取芯片上的高性能定时器.我在进行性能分析时使用它.

  • TSC计数器现在经常不可靠:它在频率改变时改变了许多处理器的速度,并且在不同的核心之间不一致,因此TSC并不总是增长. (3认同)
  • 这很有用,我会检查处理器是否是x86,因为我正在使用苹果mac进行实验...谢谢:-) (2认同)