使用C++编写计时器函数以提供纳秒级时间

111

我希望能够计算API返回值所需的时间。 这种操作所需的时间是以纳秒为单位的。由于API是一个C++类或函数,因此我将使用timer.h来进行计算:

  #include <ctime>
  #include <iostream>

  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;
  }

以上代码给出了时间的秒数。如何以纳秒为单位获得相同的时间,并获得更高的精度?


以上代码计算的是秒数,我想要得到纳秒的答案... - gagneet
1
需要将平台添加到问题中(最好也加入标题),以获得一个好的答案。 - Patrick Johnmeyer
除了获取时间之外,还需要查找微基准测试问题(这非常复杂)-仅执行一次并在开始和结束时获取时间,可能无法提供足够的精度。 - Blaisorblade
@Blaisorblade:特别是在我的一些测试中发现,clock()并不像我想象的那样快。 - Mooing Duck
17个回答

3
使用Brock Adams的方法,通过一个简单的类:
int get_cpu_ticks()
{
    LARGE_INTEGER ticks;
    QueryPerformanceFrequency(&ticks);
    return ticks.LowPart;
}

__int64 get_cpu_clocks()
{
    struct { int32 low, high; } counter;

    __asm cpuid
    __asm push EDX
    __asm rdtsc
    __asm mov counter.low, EAX
    __asm mov counter.high, EDX
    __asm pop EDX
    __asm pop EAX

    return *(__int64 *)(&counter);
}

class cbench
{
public:
    cbench(const char *desc_in) 
         : desc(strdup(desc_in)), start(get_cpu_clocks()) { }
    ~cbench()
    {
        printf("%s took: %.4f ms\n", desc, (float)(get_cpu_clocks()-start)/get_cpu_ticks());
        if(desc) free(desc);
    }
private:
    char *desc;
    __int64 start;
};

使用示例:

int main()
{
    {
        cbench c("test");
        ... code ...
    }
    return 0;
}

结果:

测试用时:0.0002毫秒

虽然有些函数调用开销,但仍应足够快 :)


3
如果您需要亚秒级精度,您需要使用特定于系统的扩展,并且必须查阅操作系统的文档。 POSIX支持使用gettimeofday达到微秒级别的精度,但由于计算机没有超过1GHz的频率,因此不能实现更高精度。如果您使用Boost,则可以查看boost::posix_time

想要保持代码的可移植性,将会查看Boost库,并检查是否可以将其与代码捆绑在一起。谢谢 :-) - gagneet

2

简约的复制粘贴结构体+懒惰使用

如果想要一个可以用于快速测试的简约结构体,建议您在C++文件中的#include后面随意复制粘贴。这是我唯一牺牲Allman风格格式的情况。

您可以在结构体的第一行轻松调整精度。可能的值为:nanoseconds, microseconds, milliseconds, seconds, minutes, 或 hours

#include <chrono>
struct MeasureTime
{
    using precision = std::chrono::microseconds;
    std::vector<std::chrono::steady_clock::time_point> times;
    std::chrono::steady_clock::time_point oneLast;
    void p() {
        std::cout << "Mark " 
                << times.size()/2
                << ": " 
                << std::chrono::duration_cast<precision>(times.back() - oneLast).count() 
                << std::endl;
    }
    void m() {
        oneLast = times.back();
        times.push_back(std::chrono::steady_clock::now());
    }
    void t() {
        m();
        p();
        m();
    }
    MeasureTime() {
        times.push_back(std::chrono::steady_clock::now());
    }
};

使用方法

MeasureTime m; // first time is already in memory
doFnc1();
m.t(); // Mark 1: next time, and print difference with previous mark
doFnc2();
m.t(); // Mark 2: next time, and print difference with previous mark
doStuff = doMoreStuff();
andDoItAgain = doStuff.aoeuaoeu();
m.t(); // prints 'Mark 3: 123123' etc...

标准输出结果

Mark 1: 123
Mark 2: 32
Mark 3: 433234

如果您想在执行后获得摘要

如果您想在之后获得报告,例如因为您的代码在其中还写入标准输出。那么请在结构体中添加以下函数(就在MeasureTime()函数之前):

void s() { // summary
    int i = 0;
    std::chrono::steady_clock::time_point tprev;
    for(auto tcur : times)
    {
        if(i > 0)
        {
            std::cout << "Mark " << i << ": "
                    << std::chrono::duration_cast<precision>(tprev - tcur).count()
                    << std::endl;
        }
        tprev = tcur;
        ++i;
    }
}

那么你可以直接使用:
MeasureTime m;
doFnc1();
m.m();
doFnc2();
m.m();
doStuff = doMoreStuff();
andDoItAgain = doStuff.aoeuaoeu();
m.m();
m.s();

这将列出所有的标记,就像以前一样,但在执行其他代码后。请注意,不应同时使用m.s()m.t()


在Ubuntu 16.04上使用OpenMP完美运行。非常感谢,我认为这应该是最佳答案! - Íhor Mé

2
如果这是针对Linux的,我一直在使用函数“gettimeofday”,它返回一个结构体,给出了自纪元以来的秒数和微秒数。然后,您可以使用timersub来减去两者的差异,并将其转换为您想要的任何时间精度。然而,您指定的是纳秒,看起来函数clock_gettime()就是您要找的。它将时间以秒和纳秒的形式放入您传递给它的结构体中。

1
现在使用clock_gettime()应该可以解决问题。我会尝试将其用于我的目的... - gagneet

2

你对此有何看法:

    int iceu_system_GetTimeNow(long long int *res)
    {
      static struct timespec buffer;
      // 
    #ifdef __CYGWIN__
      if (clock_gettime(CLOCK_REALTIME, &buffer))
        return 1;
    #else
      if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &buffer))
        return 1;
    #endif
      *res=(long long int)buffer.tv_sec * 1000000000LL + (long long int)buffer.tv_nsec;
      return 0;
    }

你能解释一下为什么要根据__CYGWIN__选择CLOCK_REALTIMECLOCK_PROCESS_CPUTIME_ID之间的区别吗? - ScumCoder

2

这里是一个很好用的Boost计时器:

//Stopwatch.hpp

#ifndef STOPWATCH_HPP
#define STOPWATCH_HPP

//Boost
#include <boost/chrono.hpp>
//Std
#include <cstdint>

class Stopwatch
{
public:
    Stopwatch();
    virtual         ~Stopwatch();
    void            Restart();
    std::uint64_t   Get_elapsed_ns();
    std::uint64_t   Get_elapsed_us();
    std::uint64_t   Get_elapsed_ms();
    std::uint64_t   Get_elapsed_s();
private:
    boost::chrono::high_resolution_clock::time_point _start_time;
};

#endif // STOPWATCH_HPP


//Stopwatch.cpp

#include "Stopwatch.hpp"

Stopwatch::Stopwatch():
    _start_time(boost::chrono::high_resolution_clock::now()) {}

Stopwatch::~Stopwatch() {}

void Stopwatch::Restart()
{
    _start_time = boost::chrono::high_resolution_clock::now();
}

std::uint64_t Stopwatch::Get_elapsed_ns()
{
    boost::chrono::nanoseconds nano_s = boost::chrono::duration_cast<boost::chrono::nanoseconds>(boost::chrono::high_resolution_clock::now() - _start_time);
    return static_cast<std::uint64_t>(nano_s.count());
}

std::uint64_t Stopwatch::Get_elapsed_us()
{
    boost::chrono::microseconds micro_s = boost::chrono::duration_cast<boost::chrono::microseconds>(boost::chrono::high_resolution_clock::now() - _start_time);
    return static_cast<std::uint64_t>(micro_s.count());
}

std::uint64_t Stopwatch::Get_elapsed_ms()
{
    boost::chrono::milliseconds milli_s = boost::chrono::duration_cast<boost::chrono::milliseconds>(boost::chrono::high_resolution_clock::now() - _start_time);
    return static_cast<std::uint64_t>(milli_s.count());
}

std::uint64_t Stopwatch::Get_elapsed_s()
{
    boost::chrono::seconds sec = boost::chrono::duration_cast<boost::chrono::seconds>(boost::chrono::high_resolution_clock::now() - _start_time);
    return static_cast<std::uint64_t>(sec.count());
}

1

plf::nanotimer 是一个轻量级的选择,适用于 Windows、Linux、Mac 和 BSD 等操作系统。根据不同的操作系统,它具有大约微秒级的精度。

  #include "plf_nanotimer.h"
  #include <iostream>

  int main(int argc, char** argv)
  {
      plf::nanotimer timer;

      timer.start()

      // Do something here

      double results = timer.get_elapsed_ns();
      std::cout << "Timing: " << results << " nanoseconds." << std::endl;    
      return 0;
  }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接