【问题标题】:C++/POSIX how to get a millisecond time-stamp the most efficient way?C ++ / POSIX如何以最有效的方式获得毫秒时间戳?
【发布时间】:2010-01-04 14:03:19
【问题描述】:

我正在为 i2c 总线操作使用开源库。这个库经常使用一个函数来获得一个毫秒分辨率的实际时间戳。

示例调用:

nowtime = timer_nowtime();
while ((i2c_CheckBit(dev) == true) && ((timer_nowtime() - nowtime) < I2C_TIMEOUT));

使用这个 i2c 库的应用程序会占用大量 CPU 容量。我发现,运行的程序最多调用函数timer_nowtime()

原函数:

unsigned long timer_nowtime(void) {        
    static bool usetimer = false;
    static unsigned long long inittime;
    struct tms cputime;

    if (usetimer == false)
    {
        inittime  = (unsigned long long)times(&cputime);
        usetimer = true;
    }

    return (unsigned long)((times(&cputime) - inittime)*1000UL/sysconf(_SC_CLK_TCK));
}

我现在的目标是,提高这个功能的效率。我是这样尝试的:

struct timespec systemtime;

clock_gettime(CLOCK_REALTIME, &systemtime);
//convert the to milliseconds timestamp
// incorrect way, because (1 / 1000000UL) always returns 0 -> thanks Pace
//return (unsigned long) ( (systemtime.tv_sec * 1000UL) + (systemtime.tv_nsec
//              * (1 / 1000000UL)));
return (unsigned long) ((systemtime.tv_sec * 1000UL)
            + (systemtime.tv_nsec / 1000000UL));

不幸的是,我不能声明这个函数inline(不知道为什么)。

哪种方法更有效地获取毫秒分辨率的实际时间戳? 我确信有一种更高效的方式来做到这一点。有什么建议么?

谢谢。

【问题讨论】:

  • 不是 (1 / 1000000UL) 总是返回 0 吗?
  • 佩斯,你是对的。 (1 / 1000000UL)= 0;我会更正我的代码。

标签: c++ linux timestamp posix


【解决方案1】:

很明显,您的示例调用在 timer_nowtime() 函数中使用了大部分 CPU 时间。您正在轮询,并且循环会占用您的 CPU 时间。您可以用更好的替代方法更换计时器功能,这样您就可以实现更多的循环迭代,但它仍然会在该功能中使用大部分 CPU 时间!更改计时器功能不会减少 CPU 时间!

您可以更改循环并引入等待时间 - 但前提是它在您的应用程序中有意义,例如:

start = timer_nowtime();
while( i2c_CheckBit(dev) ) {
    now = timer_nowtime();
    diff = now - start;
    if( diff < I2C_TIMEOUT ) break;
    else if( diff > SOME_TRESHOLD ) usleep( 1000*std::max(1,I2C_TIMEOUT-diff-SOME_SMALL_NUMBER_1_TO_10_MAYBE) );
}

计时器:我认为gettimeofday() 是一个不错的决定,它具有很高的精度,并且在大多数(所有?)Unices 中都可用。

【讨论】:

  • gettimeofday() 不是单调的,它会受到 NTP 更正等影响......这不是正确的解决方案。你想使用clock_gettime(CLOCK_MONOTONIC)
【解决方案2】:

请注意,您的两个示例并不等效; times(2) 测量 CPU 时间消耗,而 clock_gettime(CLOCK_REALTIME, ...) 测量挂钟时间。

话虽如此,clock_gettime 或较旧的 gettimeofday 等挂钟计时器通常比 times() 或 getrusage() 等 CPU 计时器快得多。

而且,正如已经指出的,您的计时功能正在占用大量 CPU 时间,因为您除了轮询它之外几乎没有做任何其他事情。如果这是一个问题,请稍等一下,例如通过调用 nanosleep() 或 clock_nanosleep()。

我认为您最好的选择是使用clock_gettime(),但是使用CLOCK_MONOTONIC 而不是CLOCK_REALTIME。

【讨论】:

  • 由于这些时间用于对硬件总线进行 bitbanging,因此挂钟时间比 CPU 时间更有意义。所以我同意你的 CLOCK_MONOTONIC 建议。
【解决方案3】:

如果您的程序仅在最新的 Intel/AMD 处理器上执行,但不是太新(时钟节流处理得不是很好),RDTSC assembly instruction 是获取时间戳的最佳方式。分辨率接近处理器的实际时钟,并且独立于系统(这意味着它也受到中断的影响。你不能吃蛋糕也不能吃)。

This page 在 C 中有一个示例。

【讨论】:

  • rdtsc 在多处理器上也可能有问题,如果您的线程碰巧切换到另一个核心/硬件线程,那么计数器值将是无意义的,因为它们在核心之间不同步。因此,在 POSIX clock_gettime() 的情况下,使用操作系统提供的 API 几乎总是更好。
  • RDTSC 不应该用于计时目的(不再......):en.wikipedia.org/wiki/Time_Stamp_Counter!
  • 我确实说过“不是最近”:)
  • 由于计时器功能用于确定经过的时间以及软件在单个 CPU 嵌入式设备上运行,因此该解决方案将是一种选择。当然,这不符合 POSIX 标准,并且很难移植到另一个平台上。
  • @Frunsi:如果你的 CPU 足够新(比如 Core2 或 Nehalem),那么rdtsc 一个时间源。不间断且不变的 TSC 意味着它始终以恒定频率滴答作响,无论实际核心时钟是否停止或运行速度快于或慢于额定/标定频率。请参阅wrong clock cycle measurements with rdtsc 了解一些历史。有一个黑暗时代,RDTSC 不能用作时间源或周期精确的性能测量,但那个时代已经过去了。现在它只是一个时间源,你需要性能计数器......
【解决方案4】:

我对 Linux 上的这一点很满意:

inline double getFractionalSeconds(void) {
   struct timeval tv;   // see gettimeofday(2)
   gettimeofday(&tv, NULL);
   double t = (double) tv.tv_sec + (double) 1e-6 * tv.tv_usec; 
   // return seconds.microseconds since epoch 
   return(t);
}

【讨论】:

    【解决方案5】:

    如果你在 Intel 上运行,你可以试试下面的代码

    unsigned long long int rdtsc(void)
    {
       unsigned long long int x;
       unsigned a, d;
    
       __asm__ volatile("rdtsc" : "=a" (a), "=d" (d));
    
       return ((unsigned long long)a) | (((unsigned long long)d) << 32);;
    }
    

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-04-08
    • 1970-01-01
    • 2022-08-14
    • 1970-01-01
    • 2013-06-22
    • 2016-09-22
    • 2015-09-22
    • 2019-08-29
    相关资源
    最近更新 更多