关于c ++:轻松测量经过的时间

Easily measure elapsed time

我正在尝试使用time()来衡量我的程序的各个点。

我不明白为什么之前和之后的值是一样的? 我知道这不是描述我的程序的最佳方式,我只想看看有多长时间。

1
2
3
4
5
6
7
8
printf("**MyProgram::before time= %ld
"
, time(NULL));

doSomthing();
doSomthingLong();

printf("**MyProgram::after time= %ld
"
, time(NULL));

我试过了:

1
2
3
4
5
6
7
8
9
10
11
12
13
struct timeval diff, startTV, endTV;

gettimeofday(&startTV, NULL);

doSomething();
doSomethingLong();

gettimeofday(&endTV, NULL);

timersub(&endTV, &startTV, &diff);

printf("**time taken = %ld %ld
"
, diff.tv_sec, diff.tv_usec);

如何读取**time taken = 0 26339的结果? 这是否意味着26,339纳秒= 26.3毫秒?

怎么样的**time taken = 4 45025,这意味着4秒和25毫秒?


1
2
3
4
5
6
7
8
9
10
11
#include <ctime>

void f() {
  using namespace std;
  clock_t begin = clock();

  code_to_time();

  clock_t end = clock();
  double elapsed_secs = double(end - begin) / CLOCKS_PER_SEC;
}

time()函数仅精确到一秒钟内,但在一秒内有CLOCKS_PER_SEC"时钟"。这是一种简单,便携的测量方法,即使它过于简单。


您可以抽象时间测量机制,并使用最少的额外代码测量每个可调用的运行时间,只需通过计时器结构调用即可。另外,在编译时,您可以参数化时序类型(毫秒,纳秒等)。

感谢Loki Astari的审核以及使用可变参数模板的建议。
这就是转发函数调用的原因。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>
#include <chrono>

template<typename TimeT = std::chrono::milliseconds>
struct measure
{
    template<typename F, typename ...Args>
    static typename TimeT::rep execution(F&& func, Args&&... args)
    {
        auto start = std::chrono::steady_clock::now();
        std::forward<decltype(func)>(func)(std::forward<Args>(args)...);
        auto duration = std::chrono::duration_cast< TimeT>
                            (std::chrono::steady_clock::now() - start);
        return duration.count();
    }
};

int main() {
    std::cout << measure<>::execution(functor(dummy)) << std::endl;
}

Demo

根据Howard Hinnant的评论,我们最好不要逃离计时系统,直到我们不得不这样做。因此,上面的类可以让用户通过提供额外的静态方法手动调用count(如C ++ 14所示)

1
2
3
4
5
6
7
8
9
10
template<typename F, typename ...Args>
static auto duration(F&& func, Args&&... args)
{
    auto start = std::chrono::steady_clock::now();
    std::forward<decltype(func)>(func)(std::forward<Args>(args)...);
    return std::chrono::duration_cast<TimeT>(std::chrono::steady_clock::now()-start);
}

// call .count() manually later when needed (eg IO)
auto avg = (measure<>::duration(func) + measure<>::duration(func)) / 2.0;

并且最适合那些客户

"want to post-process a bunch of durations prior to I/O (e.g. average)"

完整的代码可以在这里找到。我在此处记录了基于计时器构建基准测试工具的尝试。

如果C ++ 17的std::invoke可用,则可以像下面这样调用execution中的callable:

1
invoke(forward<decltype(func)>(func), forward<Args>(args)...);

提供指向成员函数的指针的callable。


1
2
3
4
5
6
7
8
//***C++11 Style:***
#include <chrono>

std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now();
std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();

std::cout <<"Time difference =" << std::chrono::duration_cast<std::chrono::microseconds>(end - begin).count() <<"[μs]" << std::endl;
std::cout <<"Time difference =" << std::chrono::duration_cast<std::chrono::nanoseconds> (end - begin).count() <<"[ns]" << std::endl;


正如我从你的问题中看到的那样,看起来你想知道执行某段代码后经过的时间。我想你会很自然地看到第二个结果。如果是这样,请尝试使用difftime()功能,如下所示。希望这能解决你的问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <time.h>
#include <stdio.h>

time_t start,end;
time (&start);
.
.
.
<your code>
.
.
.
time (&end);
double dif = difftime (end,start);
printf ("Elasped time is %.2lf seconds.", dif );


仅限Windows :(我发布此答案后添加了Linux标记)

您可以使用GetTickCount()来获取自系统启动以来经过的毫秒数。

1
2
3
4
5
long int before = GetTickCount();

// Perform time-consuming operation

long int after = GetTickCount();


time(NULL)函数将返回自1970年1月1日00:00起经过的秒数。因为,在你的程序中不同的时间调用该函数,它将始终不同
用C ++编写的时间


time(NULL)返回自1970年1月1日00:00(大纪元)以来经过的秒数。因此,两个值之间的差异是您处理的秒数。

1
2
3
4
5
6
7
int t0 = time(NULL);
doSomthing();
doSomthingLong();
int t1 = time(NULL);

printf ("time = %d secs
"
, t1 - t0);

您可以使用getttimeofday()获得更精细的结果,getttimeofday()以秒为单位返回当前时间,如time()那样以及以微秒为单位。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
struct profiler
{
    std::string name;
    std::chrono::high_resolution_clock::time_point p;
    profiler(std::string const &n) :
        name(n), p(std::chrono::high_resolution_clock::now()) { }
    ~profiler()
    {
        using dura = std::chrono::duration<double>;
        auto d = std::chrono::high_resolution_clock::now() - p;
        std::cout << name <<":"
            << std::chrono::duration_cast<dura>(d).count()
            << std::endl;
    }
};

#define PROFILE_BLOCK(pbn) profiler _pfinstance(pbn)

用法如下::

1
2
3
4
{
    PROFILE_BLOCK("Some time");
    // your code or function
}

这与RAII的范围相似

注意这不是我的,但我认为这是相关的


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#include<time.h> // for clock
#include<math.h> // for fmod
#include<cstdlib> //for system
#include <stdio.h> //for delay

using namespace std;

int main()
{


   clock_t t1,t2;

   t1=clock(); // first time capture

   // Now your time spanning loop or code goes here
   // i am first trying to display time elapsed every time loop runs

   int ddays=0; // d prefix is just to say that this variable will be used for display
   int dhh=0;
   int dmm=0;
   int dss=0;

   int loopcount = 1000 ; // just for demo your loop will be different of course

   for(float count=1;count<loopcount;count++)
   {

     t2=clock(); // we get the time now

     float difference= (((float)t2)-((float)t1)); // gives the time elapsed since t1 in milliseconds

    // now get the time elapsed in seconds

    float seconds = difference/1000; // float value of seconds
    if (seconds<(60*60*24)) // a day is not over
    {
        dss = fmod(seconds,60); // the remainder is seconds to be displayed
        float minutes= seconds/60;  // the total minutes in float
        dmm= fmod(minutes,60);  // the remainder are minutes to be displayed
        float hours= minutes/60; // the total hours in float
        dhh= hours;  // the hours to be displayed
        ddays=0;
    }
    else // we have reached the counting of days
    {
        float days = seconds/(24*60*60);
        ddays = (int)(days);
        float minutes= seconds/60;  // the total minutes in float
        dmm= fmod(minutes,60);  // the rmainder are minutes to be displayed
        float hours= minutes/60; // the total hours in float
        dhh= fmod (hours,24);  // the hours to be displayed

    }

    cout<<"Count Is :"<<count<<"Time Elapsed :"<<ddays<<" Days"<<dhh<<" hrs"<<dmm<<" mins"<<dss<<" secs";


    // the actual working code here,I have just put a delay function
    delay(1000);
    system("cls");

 } // end for loop

}// end of main


第二个程序打印的值是秒和微秒。

1
2
0 26339 = 0.026'339 s =   26339 μs
4 45025 = 4.045'
025 s = 4045025 μs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#include <ctime>
#include <cstdio>
#include <iostream>
#include <chrono>
#include <sys/time.h>
using namespace std;
using namespace std::chrono;

void f1()
{
  high_resolution_clock::time_point t1 = high_resolution_clock::now();
  high_resolution_clock::time_point t2 = high_resolution_clock::now();
  double dif = duration_cast<nanoseconds>( t2 - t1 ).count();
  printf ("Elasped time is %lf nanoseconds.
"
, dif );
}

void f2()
{
  timespec ts1,ts2;
  clock_gettime(CLOCK_REALTIME, &ts1);
  clock_gettime(CLOCK_REALTIME, &ts2);
  double dif = double( ts2.tv_nsec - ts1.tv_nsec );
  printf ("Elasped time is %lf nanoseconds.
"
, dif );
}

void f3()
{
  struct timeval t1,t0;
  gettimeofday(&t0, 0);
  gettimeofday(&t1, 0);
  double dif = double( (t1.tv_usec-t0.tv_usec)*1000);
  printf ("Elasped time is %lf nanoseconds.
"
, dif );
}
void f4()
{
  high_resolution_clock::time_point t1 , t2;
  double diff = 0;
  t1 = high_resolution_clock::now() ;
  for(int i = 1; i <= 10 ; i++)
  {
    t2 = high_resolution_clock::now() ;
    diff+= duration_cast<nanoseconds>( t2 - t1 ).count();
    t1 = t2;
  }
  printf ("high_resolution_clock:: Elasped time is %lf nanoseconds.
"
, diff/10 );
}

void f5()
{
  timespec ts1,ts2;
  double diff = 0;
  clock_gettime(CLOCK_REALTIME, &ts1);
  for(int i = 1; i <= 10 ; i++)
  {
    clock_gettime(CLOCK_REALTIME, &ts2);
    diff+= double( ts2.tv_nsec - ts1.tv_nsec );
    ts1 = ts2;
  }
  printf ("clock_gettime:: Elasped time is %lf nanoseconds.
"
, diff/10 );
}

void f6()
{
  struct timeval t1,t2;
  double diff = 0;
  gettimeofday(&t1, 0);
  for(int i = 1; i <= 10 ; i++)
  {
    gettimeofday(&t2, 0);
    diff+= double( (t2.tv_usec-t1.tv_usec)*1000);
    t1 = t2;
  }
  printf ("gettimeofday:: Elasped time is %lf nanoseconds.
"
, diff/10 );
}

int main()
{
  //  f1();
  //  f2();
  //  f3();
  f6();
  f4();
  f5();
  return 0;
}

C ++ std :: chrono具有跨平台的明显优势。
但是,与POSIX clock_gettime()相比,它还带来了显着的开销。
在我的Linux机器上,所有std::chrono::xxx_clock::now()风格的表现大致相同:

1
2
3
std::chrono::system_clock::now()
std::chrono::steady_clock::now()
std::chrono::high_resolution_clock::now()

虽然POSIX clock_gettime(CLOCK_MONOTONIC, &time)应与steady_clock::now()相同,但它的速度要快x3倍!

这是我的测试,完整性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include <stdio.h>
#include <chrono>
#include <ctime>

void print_timediff(const char* prefix, const struct timespec& start, const
struct timespec& end)
{
    double milliseconds = end.tv_nsec >= start.tv_nsec
                        ? (end.tv_nsec - start.tv_nsec) / 1e6 + (end.tv_sec - start.tv_sec) * 1e3
                        : (start.tv_nsec - end.tv_nsec) / 1e6 + (end.tv_sec - start.tv_sec - 1) * 1e3;
    printf("%s: %lf milliseconds
"
, prefix, milliseconds);
}

int main()
{
    int i, n = 1000000;
    struct timespec start, end;

    // Test stopwatch
    clock_gettime(CLOCK_MONOTONIC, &start);
    for (i = 0; i < n; ++i) {
        struct timespec dummy;
        clock_gettime(CLOCK_MONOTONIC, &dummy);
    }
    clock_gettime(CLOCK_MONOTONIC, &end);
    print_timediff("clock_gettime", start, end);

    // Test chrono system_clock
    clock_gettime(CLOCK_MONOTONIC, &start);
    for (i = 0; i < n; ++i)
        auto dummy = std::chrono::system_clock::now();
    clock_gettime(CLOCK_MONOTONIC, &end);
    print_timediff("chrono::system_clock::now", start, end);

    // Test chrono steady_clock
    clock_gettime(CLOCK_MONOTONIC, &start);
    for (i = 0; i < n; ++i)
        auto dummy = std::chrono::steady_clock::now();
    clock_gettime(CLOCK_MONOTONIC, &end);
    print_timediff("chrono::steady_clock::now", start, end);

    // Test chrono high_resolution_clock
    clock_gettime(CLOCK_MONOTONIC, &start);
    for (i = 0; i < n; ++i)
        auto dummy = std::chrono::high_resolution_clock::now();
    clock_gettime(CLOCK_MONOTONIC, &end);
    print_timediff("chrono::high_resolution_clock::now", start, end);

    return 0;
}

这是我用gcc7.2 -O3编译时得到的输出:

1
2
3
4
clock_gettime: 24.484926 milliseconds
chrono::system_clock::now: 85.142108 milliseconds
chrono::steady_clock::now: 87.295347 milliseconds
chrono::high_resolution_clock::now: 84.437838 milliseconds

time(NULL)函数调用将返回自epoc:1970年1月1日以来经过的秒数。也许您的意思是取两个时间戳之间的差异:

1
2
3
4
5
6
size_t start = time(NULL);
doSomthing();
doSomthingLong();

printf ("**MyProgram::time elapsed= %lds
"
, time(NULL) - start);

在linux上,clock_gettime()是一个不错的选择。
您必须链接实时库(-lrt)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>

#define BILLION  1000000000L;

int main( int argc, char **argv )
  {
    struct timespec start, stop;
    double accum;

    if( clock_gettime( CLOCK_REALTIME, &start) == -1 ) {
      perror("clock gettime" );
      exit( EXIT_FAILURE );
    }

    system( argv[1] );

    if( clock_gettime( CLOCK_REALTIME, &stop) == -1 ) {
      perror("clock gettime" );
      exit( EXIT_FAILURE );
    }

    accum = ( stop.tv_sec - start.tv_sec )
          + ( stop.tv_nsec - start.tv_nsec )
            / BILLION;
    printf("%lf
"
, accum );
    return( EXIT_SUCCESS );
  }


在内部,该函数将访问系统的时钟,这就是每次调用它时返回不同值的原因。通常,对于非函数式语言,函数中可能存在许多副作用和隐藏状态,仅通过查看函数的名称和参数就无法看到。


从看到的内容来看,tv_sec存储了tv_usec分别存储经过的微秒所经过的秒数。而且他们不是彼此的转变。因此,必须将它们更改为适当的单位并添加以获得总时间。

1
2
3
4
5
6
7
8
9
10
11
12
13
struct timeval startTV, endTV;

gettimeofday(&startTV, NULL);

doSomething();
doSomethingLong();

gettimeofday(&endTV, NULL);

printf("**time taken in microseconds = %ld
"
,
    (endTV.tv_sec * 1e6 + endTV.tv_usec - (startTV.tv_sec * 1e6 + startTV.tv_usec))
    );

我需要测量库中各个函数的执行时间。我不想用时间测量函数包装每个函数的每个调用因为它的丑陋并加深了调用堆栈。我也不想将定时器代码放在每个函数的顶部和底部,因为当函数可以提前退出或抛出异常时会造成混乱。所以我最终做的是制作一个使用自己的生命周期来计时的计时器。

通过这种方式,我可以通过在所讨论的代码块的开头(函数或任何范围)实例化这些对象中的一个来测量代码块的挂载时间,然后允许实例析构函数来测量自那时起所经过的时间。实例超出范围时的构造。你可以在这里找到完整的例子,但结构非常简单:

1
2
3
4
5
6
7
8
9
10
11
12
template <typename clock_t = std::chrono::steady_clock>
struct scoped_timer {
  using duration_t = typename clock_t::duration;
  const std::function<void(const duration_t&)> callback;
  const std::chrono::time_point<clock_t> start;

  scoped_timer(const std::function<void(const duration_t&)>& finished_callback) :
      callback(finished_callback), start(clock_t::now()) { }
  scoped_timer(std::function<void(const duration_t&)>&& finished_callback) :
      callback(finished_callback), start(clock_t::now()) { }
  ~scoped_timer() { callback(clock_t::now() - start); }
};

当结构超出范围时,该结构将回调所提供的仿函数,以便您可以对计时信息(打印或存储或其他)执行某些操作。如果你需要做一些更复杂的事情,你甚至可以使用带有std::placeholdersstd::bind来回调更多参数的函数。

以下是使用它的快速示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
void test(bool should_throw) {
  scoped_timer<> t([](const scoped_timer<>::duration_t& elapsed) {
    auto e = std::chrono::duration_cast<std::chrono::duration<double, std::milli>>(elapsed).count();
    std::cout <<"took" << e <<"ms" << std::endl;
  });

  std::this_thread::sleep_for(std::chrono::seconds(1));

  if (should_throw)
    throw nullptr;

  std::this_thread::sleep_for(std::chrono::seconds(1));
}

如果你想更加谨慎,你也可以使用newdelete显式启动和停止计时器,而不依赖于作用域为你做。


正如其他人已经注意到的那样,C标准库中的time()函数的分辨率不会超过一秒。可提供更好分辨率的唯一完全可移植的C功能似乎是clock(),但它测量处理器时间而不是wallclock时间。如果有人满足于将自己限制在POSIX平台(例如Linux),那么clock_gettime()函数是一个不错的选择。

从C ++ 11开始,有更好的计时工具可以提供更好的分辨率,这种形式应该在不同的编译器和操作系统中非常便携。类似地,boost :: datetime库提供了高度可移植的高分辨率时序类。

使用任何这些设施的一个挑战是通过查询系统时钟引入的时间延迟。通过试验clock_gettime(),boost :: datetime和std :: chrono,这个延迟很容易就是几微秒。因此,在测量代码的任何部分的持续时间时,您需要允许存在大约此大小的测量误差,或者尝试以某种方式校正该零误差。理想情况下,您可能希望收集函数所用时间的多个度量值,并计算多次运行所用的平均值或最大值/最小值。

为了帮助解决所有这些可移植性和统计信息收集问题,我一直在开发Github上提供的cxx-rtimers库,它试图为C ++代码的时序块提供一个简单的API,计算零错误,并报告嵌入的多个定时器的统计数据在你的代码中。如果你有一个C ++ 11编译器,你只需要#include ,并使用类似的东西:

1
2
3
4
5
void expensiveFunction() {
    static rtimers::cxx11::DefaultTimer timer("expensiveFunc");
    auto scopedStartStop = timer.scopedStart();
    // Do something costly...
}

在程序退出时,您将获得写入std :: cerr的时间统计信息摘要,例如:

1
Timer(expensiveFunc): < T > = 6.65289us, std = 3.91685us, 3.842us <= t <= 63.257us (n=731)

它显示了平均时间,标准偏差,上限和下限以及调用此函数的次数。

如果您想使用特定于Linux的计时功能,您可以#include ,或者如果您有Boost库但是使用较旧的C ++编译器,则可以#include 。还有这些计时器类的版本可以从多个线程收集统计计时信息。还有一些方法可以让您估计与系统时钟的两个连续查询相关的零误差。


它们是相同的,因为你的doSomething函数比计时器的粒度更快。尝试:

1
2
3
4
5
6
7
8
9
10
printf ("**MyProgram::before time= %ld
"
, time(NULL));

for(i = 0; i < 1000; ++i) {
    doSomthing();
    doSomthingLong();
}

printf ("**MyProgram::after time= %ld
"
, time(NULL));


我通常使用以下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <chrono>
#include <type_traits>

using perf_clock = std::conditional<
    std::chrono::high_resolution_clock::is_steady,
    std::chrono::high_resolution_clock,
    std::chrono::steady_clock
>::type;

using floating_seconds = std::chrono::duration<double>;

template<class F, class... Args>
floating_seconds run_test(Func&& func, Args&&... args)
{
   const auto t0 = perf_clock::now();
   std::forward<Func>(func)(std::forward<Args>(args)...);
   return floating_seconds(perf_clock::now() - t0);
}

它与@nikos-athanasiou提出的相同,只是我避免使用非稳定时钟并使用浮动秒数作为持续时间。


两个值相同的原因是因为你的长程序不需要那么长 - 不到一秒钟。您可以尝试在函数末尾添加一个长循环(for(int i = 0; i <100000000; i ++);)以确保这是问题,然后我们可以从那里开始......

如果以上结果是真的,你将需要找到一个不同的系统函数(我理解你在linux上工作,所以我无法帮助你使用函数名)来更准确地测量时间。我确信在linux中有一个类似于GetTickCount()的函数,你只需要找到它。


在回答OP的三个具体问题时。

"我不明白为什么之前和之后的价值是一样的?"

第一个问题和示例代码显示time()的分辨率为1秒,因此答案必须是两个函数在不到1秒的时间内执行。但偶尔它会(显然不合逻辑地)通知1秒钟两个计时器标记是否跨越一秒边界。

下一个示例使用gettimeofday()填充此结构

1
2
3
4
struct timeval {
    time_t      tv_sec;     /* seconds */
    suseconds_t tv_usec;    /* microseconds */
};

第二个问题是:"我如何读取**time taken = 0 26339的结果?这是否意味着26,339纳秒= 26.3毫秒?"

我的第二个答案是所花费的时间是0秒和26339微秒,即0.026339秒,这证明第一个例子在不到1秒的时间内执行。

第三个问题是:"**time taken = 4 45025怎么样,这意味着4秒和25毫秒?"

我的第三个答案是所花费的时间是4秒和45025微秒,即4.045025秒,这表明OP已经改变了他之前计时的两个函数所执行的任务。


您可以使用SFML库,即简单快速多媒体库。
它包括许多有用且定义良好的类,如Clock,Socket,Sound,Graphics等。
它非常易于使用且强烈推荐。

这是这个问题的一个例子。

1
2
3
4
5
sf::Clock clock;
...
Time time1 = clock.getElapsedTime();
...
Time time2 = clock.restart();