2008-12-11 143 views
104

只使用ANSI C,有没有什么方法可以用毫秒精度或更高来测量时间?我在浏览time.h,但我只找到第二个精度函数。如何使用ANSI C测量以毫秒为单位的时间?

+4

请注意精度和精度之间的差异。你可以通过以秒为单位的时间乘以1000来得到毫秒*精度*的时间,但这没用。 ms精度函数不一定具有ms精度 - 虽然它们通常比1s精度要好。 – 2008-12-13 02:22:53

+2

简单的答案是NO,ANSI C不支持毫秒精度或更好。更复杂的答案取决于你想要做什么 - 坦率地说整个区域是一场噩梦 - 即使你允许使用广泛可用的Posix功能。你使用术语“测量”,所以我假设你对间隔而不是“挂钟”时间感兴趣。但是,你是否试图通过你的过程来测量绝对时间段或CPU使用情况? – Dipstick 2009-08-09 09:22:39

+0

只是想对特种部队说只是保存了我的培根,再次;-) – corlettk 2011-05-16 07:31:22

回答

79

没有提供优于1秒时间分辨率的ANSI C函数,但POSIX函数gettimeofday提供了微秒的分辨率。时钟功能仅测量进程花费的时间量,并且在许多系统上不准确。

您可以使用此功能是这样的:

struct timeval tval_before, tval_after, tval_result; 

gettimeofday(&tval_before, NULL); 

// Some code you want to time, for example: 
sleep(1); 

gettimeofday(&tval_after, NULL); 

timersub(&tval_after, &tval_before, &tval_result); 

printf("Time elapsed: %ld.%06ld\n", (long int)tval_result.tv_sec, (long int)tval_result.tv_usec); 

这在我的机器上返回Time elapsed: 1.000870

+22

轻微警告:gettimeofday()不是单调的,这意味着它可以跳过(甚至倒退),例如,如果您的机器试图保持同步与网络时间服务器或其他时间源。 – Dipstick 2009-08-09 09:30:01

+3

准确地说:在ISO C99(我认为这与ANSI C的这部分兼容)中,甚至没有任何*时间分辨率的保证。 (ISO C99,7.23.1p4) – 2010-11-26 23:28:31

+10

一个小用法示例会非常漂亮...... – 2012-04-01 18:43:00

4

您可能获得的最佳精度是通过使用x86提供的“rdtsc”指令,它可以提供时钟级别的分辨率(当然,ne必须考虑到rdtsc调用本身的成本,它可以在应用程序启动时轻松测量)。

这里的主要问题是测量每秒钟的时钟数量,这应该不会太难。

42
#include <time.h> 
clock_t uptime = clock()/(CLOCKS_PER_SEC/1000); 
25

我总是使用clock_gettime()函数,从CLOCK_MONOTONIC时钟返回时间。返回的时间是以秒和纳秒为单位的时间量,因为过去有一些未指定的时间点,例如时代的系统启动。

#include <stdio.h> 
#include <stdint.h> 
#include <time.h> 

int64_t timespecDiff(struct timespec *timeA_p, struct timespec *timeB_p) 
{ 
    return ((timeA_p->tv_sec * 1000000000) + timeA_p->tv_nsec) - 
      ((timeB_p->tv_sec * 1000000000) + timeB_p->tv_nsec); 
} 

int main(int argc, char **argv) 
{ 
    struct timespec start, end; 
    clock_gettime(CLOCK_MONOTONIC, &start); 

    // Some code I am interested in measuring 

    clock_gettime(CLOCK_MONOTONIC, &end); 

    uint64_t timeElapsed = timespecDiff(&end, &start); 
} 
-3

在windows下:

SYSTEMTIME t; 
GetLocalTime(&t); 
swprintf_s(buff, L"[%02d:%02d:%02d:%d]\t", t.wHour, t.wMinute, t.wSecond, t.wMilliseconds); 
4

timespec_get从C11回报高达纳秒,四舍五入为执行决议。

看起来像来自POSIX'clock_gettime的ANSI ripoff。

示例:printf是每100ms进行在Ubuntu 15.10:

#include <stdio.h> 
#include <stdlib.h> 
#include <time.h> 

static long get_nanos(void) { 
    struct timespec ts; 
    timespec_get(&ts, TIME_UTC); 
    return (long)ts.tv_sec * 1000000000L + ts.tv_nsec; 
} 

int main(void) { 
    long nanos; 
    long last_nanos; 
    long start; 
    nanos = get_nanos(); 
    last_nanos = nanos; 
    start = nanos; 
    while (1) { 
     nanos = get_nanos(); 
     if (nanos - last_nanos > 100000000L) { 
      printf("current nanos: %ld\n", nanos - start); 
      last_nanos = nanos; 
     } 
    } 
    return EXIT_SUCCESS; 
} 

C11 N1570 standard draft7.27.2.5的timespec_get功能说:

如果碱是TIME_UTC,所述tv_sec构件是设置为从 实现定义的时期开始的秒数,截断为整个值,并且tv_nsec成员为 ,设置为整数纳秒,舍入到系统时钟的分辨率。 (321)

321)尽管结构timespec对象描述纳秒级分辨率的时间,但可用的分辨率是依赖于系统的,甚至可能大于1秒。

C++ 11也得到了std::chrono::high_resolution_clockC++ Cross-Platform High-Resolution Timer

的glibc 2。21sysdeps/posix/timespec_get.c下实现它为:

int 
timespec_get (struct timespec *ts, int base) 
{ 
    switch (base) 
    { 
    case TIME_UTC: 
     if (__clock_gettime (CLOCK_REALTIME, ts) < 0) 
     return 0; 
     break; 

    default: 
     return 0; 
    } 

    return base; 
} 

如此明确:

10

实现一个便携式解决方案

因为它已经在这里提到的是有足够的精度没有适当的ANSI解决方案的时间测量的问题,我想撰写关于如何获得便携式(如果可能)高分辨率时间测量解决方案的方法。

单调时钟与时间戳

一般来说有时间测量的方法有两种:

  • 单调时钟;
  • 当前(日期)时间戳。

第一个使用一个单调时钟计数器(有时被称为刻度计数器),该计数与一个预定义的频率蜱,所以如果你有一个蜱值和频率是已知的,就可以很容易地蜱转换为经过的时间。实际上并不能保证单调时钟以任何方式反映当前系统时间,它也可以计数自系统启动以来的时钟周期。但它保证了时钟总是以不断增加的方式运行,而不管系统状态如何。通常频率被绑定到硬件高分辨率信号源,这就是为什么它提供高精度(取决于硬件,但大多数现代硬件在高分辨率时钟源方面没有问题)。

第二种方式提供基于当前系统时钟值的(日期)时间值。它也可能具有高分辨率,但它有一个主要缺点:这种时间值可能受到不同系统时间调整的影响,即时区变化,夏令时(DST)更改,NTP服务器更新,系统休眠等等上。在某些情况下,您可能会得到一个可能导致未定义行为的经过时间负值。实际上这种时间源比第一个时间源不可靠。

因此,时间间隔测量的第一条规则是尽可能使用单调时钟。它通常具有很高的精度,并且通过设计可靠。

后备战略

在实现便携的解决方案是值得考虑后备策略:如果有使用单调时钟和后备时间标记的方法,如果在系统中没有单调时钟。

的Windows

有许多关于Windows时测量MSDN上称为Acquiring high-resolution time stamps一个伟大的文章描述,您可能需要了解软件和硬件支持的所有细节。为了获得在Windows高精度时间标记,你应该:

  • 查询定时器的频率(每秒蜱)与QueryPerformanceFrequency

    LARGE_INTEGER tcounter; 
    LARGE_INTEGER freq;  
    
    if (QueryPerformanceFrequency (&tcounter) != 0) 
        freq = tcounter.QuadPart; 
    

    定时器频率固定在系统启动,所以你需要得到它只有一次。

  • 查询当前刻度值与QueryPerformanceCounter

    LARGE_INTEGER tcounter; 
    LARGE_INTEGER tick_value; 
    
    if (QueryPerformanceCounter (&tcounter) != 0) 
        tick_value = tcounter.QuadPart; 
    
  • 规模蜱经过的时间,即到微秒:

    LARGE_INTEGER usecs = (tick_value - prev_tick_value)/(freq/1000000); 
    

据微软,你不应该有任何在大多数情况下,Windows XP和更高版本的这种方法存在问题。但是您也可以在Windows上使用两种备用解决方案:

  • GetTickCount提供自系统启动以来经过的毫秒数。它每49.7天包装一次,所以要小心测量更长的时间间隔。
  • GetTickCount64GetTickCount的64位版本,但它可从Windows Vista及更高版本开始使用。

OS X(MACOS)

OS X(MACOS)具有其自己的马赫绝对时间为单位代表的单调时钟。开始的最佳方式是苹果公司的文章Technical Q&A QA1398: Mach Absolute Time Units,它描述了(使用代码示例)如何使用特定于Mach的API来获取单调刻度。还有一个关于它的地方性问题,叫做clock_gettime alternative in Mac OS X,最后可能会让你对可能出现的数值溢出感到困惑,因为计数器频率是以分子和分母的形式使用的。因此,短的例子,如何让经过时间:

  • 获得时钟频率分子和分母:

    #include <mach/mach_time.h> 
    #include <stdint.h> 
    
    static uint64_t freq_num = 0; 
    static uint64_t freq_denom = 0; 
    
    void init_clock_frequency() 
    { 
        mach_timebase_info_data_t tb; 
    
        if (mach_timebase_info (&tb) == KERN_SUCCESS && tb.denom != 0) { 
         freq_num = (uint64_t) tb.numer; 
         freq_denom = (uint64_t) tb.denom; 
        } 
    } 
    

    你需要做的只有一次。

  • 查询当前刻度值与mach_absolute_time

    uint64_t tick_value = mach_absolute_time(); 
    
  • 规模蜱经过的时间,即到微秒,使用以前查询分子和分母:

    uint64_t value_diff = tick_value - prev_tick_value; 
    
    /* To prevent overflow */ 
    value_diff /= 1000; 
    
    value_diff *= freq_num; 
    value_diff /= freq_denom; 
    

    主要想法防止溢出是在使用分子和分母之前将滴答降低到所需的准确度。由于最初的定时器分辨率是纳秒,所以我们将它除以1000得到微秒。您可以在Chromium的time_mac.c中找到相同的方法。如果您确实需要纳秒精度,请考虑阅读How can I use mach_absolute_time without overflowing?

Linux和UNIX

clock_gettime调用任何POSIX友好的系统上的最佳方式。它可以从不同的时钟源查询时间,我们需要的是CLOCK_MONOTONIC。不具有clock_gettime支持CLOCK_MONOTONIC,所以你需要做的第一件事就是检查它的可用性所有系统:

  • 如果_POSIX_MONOTONIC_CLOCK被定义为一个值>= 0这意味着CLOCK_MONOTONIC是缴费;
  • 如果_POSIX_MONOTONIC_CLOCK被定义为0这意味着你应该另外检查是否正常工作在运行时,我建议使用sysconf

    #include <unistd.h> 
    
    #ifdef _SC_MONOTONIC_CLOCK 
    if (sysconf (_SC_MONOTONIC_CLOCK) > 0) { 
        /* A monotonic clock presents */ 
    } 
    #endif 
    
  • 否则单调的时钟不支持,你应该使用后备策略(见下文)。

clock_gettime使用是非常简单的:

  • 获取时间值:

    #include <time.h> 
    #include <sys/time.h> 
    #include <stdint.h> 
    
    uint64_t get_posix_clock_time() 
    { 
        struct timespec ts; 
    
        if (clock_gettime (CLOCK_MONOTONIC, &ts) == 0) 
         return (uint64_t) (ts.tv_sec * 1000000 + ts.tv_nsec/1000); 
        else 
         return 0; 
    } 
    

    我在这里缩小时间到微秒。

  • 计算与前次值之差获得同样的方式:

    uint64_t prev_time_value, time_value; 
    uint64_t time_diff; 
    
    /* Initial time */ 
    prev_time_value = get_posix_clock_time(); 
    
    /* Do some work here */ 
    
    /* Final time */ 
    time_value = get_posix_clock_time(); 
    
    /* Time difference */ 
    time_diff = time_value - prev_time_value; 
    

最好的后备策略是使用gettimeofday呼叫:它不是单调的,但它提供了相当好的分辨率。我们的想法是一样的与clock_gettime,但要获得一个时间值,你应该:

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

uint64_t get_gtod_clock_time() 
{ 
    struct timeval tv; 

    if (gettimeofday (&tv, NULL) == 0) 
     return (uint64_t) (tv.tv_sec * 1000000 + tv.tv_usec); 
    else 
     return 0; 
} 

同样,时间值缩减到微秒。

SGI IRIX

IRIXclock_gettime通话,但它缺乏CLOCK_MONOTONIC。相反,它有自己的定义为CLOCK_SGI_CYCLE的单调时钟源,您应该使用它代替CLOCK_MONOTONICclock_gettime

Solaris和HP-UX

的Solaris有它自己的高分辨率定时器接口gethrtime它返回纳秒当前定时器值。虽然较新版本的Solaris可能有clock_gettime,但如果您需要支持旧的Solaris版本,则可以坚持使用gethrtime

用法很简单:

#include <sys/time.h> 

void time_measure_example() 
{ 
    hrtime_t prev_time_value, time_value; 
    hrtime_t time_diff; 

    /* Initial time */ 
    prev_time_value = gethrtime(); 

    /* Do some work here */ 

    /* Final time */ 
    time_value = gethrtime(); 

    /* Time difference */ 
    time_diff = time_value - prev_time_value; 
} 

HP-UX缺乏clock_gettime,但它支持gethrtime,你应该以同样的方式为在Solaris上使用。

BeOS的

BeOS也有自己的高分辨率定时器接口system_time返回微秒数量距今已有计算机被启动。

实例:

#include <kernel/OS.h> 

void time_measure_example() 
{ 
    bigtime_t prev_time_value, time_value; 
    bigtime_t time_diff; 

    /* Initial time */ 
    prev_time_value = system_time(); 

    /* Do some work here */ 

    /* Final time */ 
    time_value = system_time(); 

    /* Time difference */ 
    time_diff = time_value - prev_time_value; 
} 

OS/2

OS/2有其自己的API,以检索高精度时间戳:

  • 查询定时器频率(每蜱单位)与DosTmrQueryFreq(用于GCC编译器):

    #define INCL_DOSPROFILE 
    #define INCL_DOSERRORS 
    #include <os2.h> 
    #include <stdint.h> 
    
    ULONG freq; 
    
    DosTmrQueryFreq (&freq); 
    
  • 查询当前蜱值与DosTmrQueryTime

    QWORD tcounter; 
    unit64_t time_low; 
    unit64_t time_high; 
    unit64_t timestamp; 
    
    if (DosTmrQueryTime (&tcounter) == NO_ERROR) { 
        time_low = (unit64_t) tcounter.ulLo; 
        time_high = (unit64_t) tcounter.ulHi; 
    
        timestamp = (time_high << 32) | time_low; 
    } 
    
  • 规模蜱经过时间,即,毫秒:

    uint64_t usecs = (prev_timestamp - timestamp)/(freq/1000000); 
    

实施例实施

您可以查看实现上述所有策略的plibsys库(有关详细信息,请参阅ptimeprofiler * .c)。

相关问题