在Linux上获取毫秒级时间的C++方法——clock()似乎无法正常工作

105
在Windows上,clock()返回毫秒级的时间,但在我正在使用的这个Linux框,它将其舍入到最近的1000,因此精度仅到"秒"级别而不是毫秒级别。
我找到了一个使用Qt解决的方法,使用QTime类来实例化一个对象,在调用start()之后调用elapsed()以获取经过的毫秒数。
我有点幸运,因为我一开始就在使用Qt,但我想要一个不依赖第三方库的解决方案。
难道没有标准的方法吗?
更新
请不要推荐Boost..
如果Boost和Qt都可以做到这一点,那么肯定不是魔法,一定有一些标准的东西可以使用!

2
关于编辑 - 但以可移植的方式进行编辑有些麻烦。 - Anonymous
相关:https://dev59.com/vF4c5IYBdhLWcg3wCGb2为什么clock被认为是不好的。 - Cole Tobin
16个回答

140
#include <sys/time.h>
#include <stdio.h>
#include <unistd.h>
int main()
{
    struct timeval start, end;

    long mtime, seconds, useconds;    

    gettimeofday(&start, NULL);
    usleep(2000);
    gettimeofday(&end, NULL);

    seconds  = end.tv_sec  - start.tv_sec;
    useconds = end.tv_usec - start.tv_usec;

    mtime = ((seconds) * 1000 + useconds/1000.0) + 0.5;

    printf("Elapsed time: %ld milliseconds\n", mtime);

    return 0;
}

4
为什么你要在差值上加上+0.5? - Mahmoud Al-Qudsi
21
@电脑专家,这是一种常见的取整技巧。当数值被截断为整数时,加法之前在0.0到0.4999之间的值会被截断为0,在0.5到0.9999之间的值会被截断为1。 - Mark Ransom
8
tv_usec 不是毫秒,而是微秒。 - NebulaFox
13
严肃工作时很糟糕。每年两次出现大问题,当有人执行“date -s”命令时,当然还有NTP同步。 - AndrewStone
15
@AndrewStone是正确的,使用clock_gettime(2)和CLOCK_REALTIME在同一台计算机上比较时间。从gettimeofday(2)手册中可以看到:“ POSIX.1-2008将gettimeofday(2)标记为过时,并建议改用clock_gettime(2)。”@CTT,请更新示例,将“struct timeval”更改为“struct timespec”,并将“ gettimeofday(&start,NULL)”更改为“clock_gettime(CLOCK_MONOTONIC,&start)”,以便人们不会遇到麻烦。 - Bobby Powers
显示剩余2条评论

60

请注意,clock不测量墙钟时间。这意味着如果您的程序需要5秒钟,clock未必会测量出5秒钟,而可能更多(如果您的程序运行了多个线程并且使用了更多的CPU时间)或者更少(如果程序在等待某个事件时没有使用CPU时间)。它测量的是使用的CPU时间的近似值。若要看到差异,请考虑以下代码:

#include <iostream>
#include <ctime>
#include <unistd.h>

int main() {
    std::clock_t a = std::clock();
    sleep(5); // sleep 5s
    std::clock_t b = std::clock();

    std::cout << "difference: " << (b - a) << std::endl;
    return 0;
}

这在我的系统上输出

$ difference: 0

因为我们只是在睡眠中,而没有使用任何CPU时间!然而,使用gettimeofday,我们可以得到我们想要的(?)

#include <iostream>
#include <ctime>
#include <unistd.h>
#include <sys/time.h>

int main() {
    timeval a;
    timeval b;

    gettimeofday(&a, 0);
    sleep(5); // sleep 5s
    gettimeofday(&b, 0);

    std::cout << "difference: " << (b.tv_sec - a.tv_sec) << std::endl;
    return 0;
}

我的系统上的输出

$ difference: 5
如果你需要更高的精度,但是想要获取“CPU时间”,那么你可以考虑使用getrusage函数。

关于提到 sleep() - 我已经在想要问一个问题(为什么它对每个人都有效,除了我?!),当我发现了你的答案。 - Hi-Angel

38
你可以在你的方法开头和结尾使用gettimeofday,然后计算两个返回结构体之间的差异。你将会得到以下类似的结构体:
struct timeval {
  time_t tv_sec;
  suseconds_t tv_usec;
}

编辑:正如下面两条评论所建议的那样,如果您有可用的话,则clock_gettime(CLOCK_MONOTONIC)是更好的选择,这几乎在现在到处都可以使用。

编辑:其他人评论说,您还可以使用具有std::chrono :: high_resolution_clock的现代C ++,但不能保证时钟单调。请改用steady_clock。


38
不适合严肃工作。每年两次出现大问题,当有人执行“date -s”命令时,以及当然还有NTP同步。请使用clock_gettime(CLOCK_MONOTONIC)函数。 - AndrewStone
9
UNIX 时间每年甚至不会两次变化。但是,确实使用“CLOCK_MONOTONIC”可以避免本地化系统时间的调整,这非常好。 - Lightness Races in Orbit

18

我也推荐使用Boost提供的工具。可以使用之前提到的Boost Timer,或者从Boost.DateTime中创建一些内容,或者在sandbox中使用新的提议库 - Boost.Chrono:这最后一个将取代Timer并具有以下特点:

  • C++0x标准库中的时间工具,包括:
    • 类模板duration
    • 类模板time_point
    • Clocks:
      • system_clock
      • monotonic_clock
      • high_resolution_clock
  • 类模板timer,带有typedefs:
    • system_timer
    • monotonic_timer
    • high_resolution_timer
  • 进程时钟和计时器:
    • process_clock,捕获实际、用户CPU和系统CPU时间。
    • process_timer,捕获经过的实际、用户CPU和系统CPU时间。
    • run_timer,方便地报告|process_timer|结果。
  • C++0x标准库的编译时有理算术。

这里是特性列表的来源。


目前您可以使用Boost Timer,然后在Chrono被审查/接受时进行平稳迁移。 - Anonymous

13

我基于CTT的回答编写了一个Timer类。可以按以下方式使用:

Timer timer = Timer();
timer.start();
/* perform task */
double duration = timer.stop();
timer.printTime(duration);

这是它的实现:

#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
using namespace std;

class Timer {
private:

    timeval startTime;

public:

    void start(){
        gettimeofday(&startTime, NULL);
    }

    double stop(){
        timeval endTime;
        long seconds, useconds;
        double duration;

        gettimeofday(&endTime, NULL);

        seconds  = endTime.tv_sec  - startTime.tv_sec;
        useconds = endTime.tv_usec - startTime.tv_usec;

        duration = seconds + useconds/1000000.0;

        return duration;
    }

    static void printTime(double duration){
        printf("%5.6f seconds\n", duration);
    }
};

2
这很酷,但是“nseconds”是误导性的,因为timeval不包含纳秒,它包含微秒,所以我建议人们称其为“useconds”。 - pho0

9

如果您不需要将代码移植到旧版Unix系统,可以使用clock_gettime()函数,它可以提供纳秒级别的时间(如果您的处理器支持该分辨率)。它是POSIX标准中的一部分,但是从2001年开始。


4

使用C++11和std::chrono::high_resolution_clock,您可以这样做:

#include <iostream>
#include <chrono>
#include <thread>
typedef std::chrono::high_resolution_clock Clock;

int main()
{
    std::chrono::milliseconds three_milliseconds{3};

    auto t1 = Clock::now();
    std::this_thread::sleep_for(three_milliseconds);
    auto t2 = Clock::now();

    std::cout << "Delta t2-t1: " 
              << std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count()
              << " milliseconds" << std::endl;
}

输出:

Delta t2-t1: 3 milliseconds

链接到演示: http://cpp.sh/2zdtu

4

clock()函数的分辨率通常相当糟糕。如果您想要以毫秒级别测量时间,一种替代方法是使用clock_gettime()函数,如此问题中所述。

(请记住,在Linux上需要链接-lrt。)


2

这应该可以运行……在Mac上测试过了……


#include <stdio.h>
#include <sys/time.h>

int main() {
        struct timeval tv;
        struct timezone tz;
        struct tm *tm;
        gettimeofday(&tv,&tz);
        tm=localtime(&tv.tv_sec);
        printf("StartTime: %d:%02d:%02d %d \n", tm->tm_hour, tm->tm_min, tm->tm_sec, tv.tv_usec);
}

对的...运行两次并相减...


2

在Linux系统上,clock()函数不会返回毫秒或秒。通常情况下,该函数返回微秒。正确解释clock()函数返回值的方式是将其除以CLOCKS_PER_SEC以确定已经过去了多少时间。


不要在我正在工作的框中打印这个!而且,我确实在除以CLOCKS_PER_SEC,但这是无意义的,因为分辨率只有到秒。 - hasen
说实话,单位确实是微秒(在所有POSIX系统上CLOCKS_PER_SEC为1000000)。只是它的分辨率是秒。:-P。 - Evan Teran

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