轻松测量经过时间

403

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

我不明白的是为什么“before”和“after”的值是相同的?我知道这不是最好的程序性能分析方法,但我只是想看一下某些操作需要多长时间。

printf("**MyProgram::before time= %ld\n", time(NULL));

doSomthing();
doSomthingLong();

printf("**MyProgram::after time= %ld\n", time(NULL));
我已经尝试过:
struct timeval diff, startTV, endTV;

gettimeofday(&startTV, NULL); 

doSomething();
doSomethingLong();

gettimeofday(&endTV, NULL); 

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

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

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

** time taken = 4 45025又是怎样的含义?它是否代表了4秒又25毫秒?


10
我不理解这个问题。当然,这些值是不同的。由于经过了一段时间,所以time()会返回一个不同的值。 - Thomas
1
你说的“我不明白的是为什么before和after的值不同”是什么意思?你正在使用time(NULL)获取当前时间(自1970年1月1日以来的秒数)...第二次调用它将在第一次之后N秒,因此...不同(除非你所做的事情不需要一秒钟完成...在这种情况下,它将与第一个相同)。 - Brian Roach
1
你能告诉我们它打印了什么,如果用秒表、挂钟(或日历)计时,需要多长时间吗? - Matt Curtis
4
抱歉,我的意思是这两个值是相同的。我打错了问题。 - hap497
2
请参见此主题:https://dev59.com/MXVC5IYBdhLWcg3wfxQ8 - default
显示剩余7条评论
26个回答

557
//***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;

33
为了运行这个程序,你需要添加 #include <chrono> 指令,并将报告时间更改为:std::cout << "Time difference (sec) = " << (std::chrono::duration_cast<std::chrono::microseconds>(end - begin).count()) /1000000.0 <<std::endl; (编译时不要忘记加上C++11标志:-std=c++11)。 - Antonello
4
根据cppreference的文档,“这个时钟与挂钟时间无关(例如,它可以是自上次重启以来的时间),最适合用于测量间隔。” - cylus
2
@AxelRietschin 这只会返回秒数。Antonello的建议还提供了小数时间(即2.15315秒,而不仅仅是2秒)。 - rayryeng
4
这是什么数据类型?std::chrono::duration_caststd::chrono::microseconds(end - begin).count()这是一个用于计算时间差的数据类型,它将两个时间点相减后转换为微秒,并返回微秒数。在C++中使用std::chrono库进行操作。 - sqp_125
@sqp_125 长整型 - otto
显示剩余5条评论

340

0 - Delta

使用delta函数来计算时间差异:

auto start = std::chrono::steady_clock::now();
std::cout << "Elapsed(ms)=" << since(start).count() << std::endl;

since 接受任何时间点并生成任何持续时间(毫秒是默认值)。它的定义如下:

template <
    class result_t   = std::chrono::milliseconds,
    class clock_t    = std::chrono::steady_clock,
    class duration_t = std::chrono::milliseconds
>
auto since(std::chrono::time_point<clock_t, duration_t> const& start)
{
    return std::chrono::duration_cast<result_t>(clock_t::now() - start);
}

演示

1 - 计时器

使用基于std::chrono的计时器:

Timer clock; // Timer<milliseconds, steady_clock>

clock.tick();
/* code you want to measure */
clock.tock();

cout << "Run time = " << clock.duration().count() << " ms\n";

演示

计时器的定义如下:

template <class DT = std::chrono::milliseconds,
          class ClockT = std::chrono::steady_clock>
class Timer
{
    using timep_t = typename ClockT::time_point;
    timep_t _start = ClockT::now(), _end = {};

public:
    void tick() { 
        _end = timep_t{}; 
        _start = ClockT::now(); 
    }
    
    void tock() { _end = ClockT::now(); }
    
    template <class T = DT> 
    auto duration() const { 
        gsl_Expects(_end != timep_t{} && "toc before reporting"); 
        return std::chrono::duration_cast<T>(_end - _start); 
    }
};

正如Howard Hinnant所指出的那样,我们使用持续时间来保持在chrono类型系统中,并执行诸如平均值或比较之类的操作(例如,在这里这意味着使用std::chrono::milliseconds)。当我们只进行IO时,我们使用持续时间的count()或滴答数(例如,在这里是毫秒数)。

2 - 仪器化

任何可调用对象(函数、函数对象、lambda等)都可以被仪器化以进行基准测试。假设您有一个带有参数arg1,arg2的可调用函数F,则此技术的结果为:

cout << "F runtime=" << measure<>::duration(F, arg1, arg2).count() << "ms";

演示

measure 的定义如下:

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

如(1)所述,使用不带.count()的持续时间对于那些想要在I/O之前进行一堆持续时间的后处理的客户端最为有用,例如平均值:
auto avg = (measure<>::duration(func) + measure<>::duration(func)) / 2;
std::cout << "Average run time " << avg.count() << " ms\n";

+这就是为什么要转发函数调用。

+完整代码可以在这里找到。

+我尝试基于chrono构建一个基准测试框架的记录可以在这里找到。

+旧版演示


2
不错;我在我的代码中有类似的东西,但是使用了一个不同于该类的接口:我有一个名为 code_timer 的类,它在构造函数中获取开始时间 (std::chrono::system_clock::now();),一个 code_timer::ellapsed 方法来测量新的 now() 调用和构造函数中的差异,并且一个 code_timer::reset 方法来将开始时间重置为新的 now() 结果。为了测量我的代码中函数对象的执行时间,我使用了一个类外的自由函数。这允许从对象的构造到异步调用完成的时间进行测量。 - utnapistim
9
小细节:在必要之前不要跳出chrono系统(避免使用.count())。当被迫时让客户端调用.count()(例如I/O,这确实很不幸)。客户端可能希望在I/O之前对一堆持续时间进行后处理(例如平均值),最好在chrono系统内完成。 - Howard Hinnant
1
@user3241228 抱歉,删去第2点;这里是原因。 - Nikos Athanasiou
2
为什么不使用std::forward<F>(func) - oliora
3
这句话的意思是:“这两个东西是一样的。我更喜欢使用std::forward<decltype(func)>(func),因为它可以适用于通用lambda的参数 (auto&& func),在那里 F 在语法上不存在,并且很容易抽象成实用的宏定义 #define fw(arg) std::forward<decltype(arg)>(arg),我在我的基准库中使用了这种方式(所以这是一个语法残留,在答案中我没有详细解释)。" - Nikos Athanasiou
显示剩余22条评论

285
#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个“时钟节拍”。这是一种简单、便携的测量方法,尽管它过于简化。


142
请注意,clock() 测量的是 CPU 时间,而不是实际经过的时间(后者可能要大得多)。 - jlstrecker
14
在为集群编写并行代码时,这种方法不能反映现实世界的时间... - Nicholas Hamilton
3
这似乎是最简单的方法。你愿意更新或回复@jlstrecker发表的评论吗? - Lorah Attkins
6
上面发布的解决方案由于多种原因不是一个好的解决方案。这是正确的答案- https://dev59.com/lHA85IYBdhLWcg3wCfLm#2962914 - Xofo
1
我尝试了这个解决方案,正如评论所建议的那样,我的计时器运行的速度比现实时间快得多。 - RTbecard
显示剩余3条评论

59

根据您的问题,我看到您想知道某些代码执行后经过的时间。我猜您希望以秒为单位查看结果。如果是这样,请尝试使用如下所示的difftime()函数。希望这可以解决您的问题。

#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 );

4
这总是给我整数秒,这应该是正常的吗? - sodiumnitrate
14
时间始终只返回秒数,因此不能用于亚秒测量。 - DeepDeadpool

31

仅适用于 Windows:(在我发布此回答后添加了 Linux 标签)

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

long int before = GetTickCount();

// Perform time-consuming operation

long int after = GetTickCount();

7
我正在Linux上使用它,所以无法使用GetTickCount()函数。 - hap497
1
已经没关系了 ;) 感谢您更新帖子的标签。 - RvdK
它可以工作并提供实时时间,而不是CPU时间。我通过将SleepEx(5000,0)放置在//执行耗时操作的位置进行了测试,并且“after”和“before”的差异几乎为5秒。 - Ruchir
在SDL中有一个可移植的函数可以做同样的事情:SDL_GetTicks()。它测量自SDL初始化以来的时间,而不是系统启动时间。 - val is still with Monica

18
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)

使用方法如下 ::

{
    PROFILE_BLOCK("Some time");
    // your code or function
}

这类似于作用域内的RAII

注意:这不是我的,但我觉得它在这里很相关


2
包括缺失 - Stepan Yakovenko

16

time(NULL) 函数返回自 1970 年 01 月 01 日 00:00 开始经过的秒数(Unix 时间戳)。因此两个值之间的差异即为处理所需的秒数。

int t0 = time(NULL);
doSomthing();
doSomthingLong();
int t1 = time(NULL);

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

您可以使用gettimeofday()获得更细致的结果,它返回当前时间的秒数,就像time()一样,并且还返回微秒。


似乎这是最明智的方式,而且非常类似于Java。顺便问一下,这是否应该存储在“long”类型中? - Lucas Sousa

13

time(NULL)函数将返回自1970年01月01日00:00以来经过的秒数。由于该函数在程序中的不同时间被调用,因此它的返回值总是不同的。

C++中的时间

我不知道为什么有人给你点了个踩,但是你的回答并不完全正确。首先它没有返回日期时间,并且它并不总是不同的。 - Matt Joiner

9
#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 

3
感谢您的回答,但我们更喜欢有一个包含代码简要描述的前言。谢谢。 - Kev
3
这不是已经流逝的时间,而是处理器时间。 - JonnyJD

8
您的第二个程序打印的值是秒和微秒。
0 26339 = 0.026'339 s =   26339 µs
4 45025 = 4.045'025 s = 4045025 µs

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