在Linux和OS X上使用C语言实现ns-precision单调时钟

4

clock_gettime(CLOCK_MONOTONIC, ...)在Linux上可用,但在OS X上不可用。 Mach计时器在OS X上可用,但在Linux上不可用。

我如何在C语言中获得一个同时适用于Linux和OS X的纳秒级单调时钟?


这是受到这个问题和答案的启发。 - Douglas B. Staple
1
重要提醒:虽然Linux单调时钟承诺确实是单调的(如果您需要在用户或NTP客户端可能重置时钟的计算机上保持稳定性,则这一点非常重要),并且更精确,但实际上并不保证更准确。这取决于操作系统构建和运行的硬件的细节。通常分辨率也会更好,但可能不等于精度;一些低位可能是噪声,就像msec时钟的一些低位可能是噪声一样。如果这很重要,请检查平台如何定义它。 - keshlam
@keshlam 不是的,在Linux中,所有其他时钟都源自单调时钟,这可以在内核源代码中看到。 - mirabilos
2个回答

9
/* 
This is based on the snippet current_utc_time.c from:
https://gist.github.com/jbenet/1087739

On OS X, compile with: gcc get_monotonic_time.c
   Linux, compile with: gcc get_monotonic_time.c -lrt
*/

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

#ifdef __MACH__
#include <mach/clock.h>
#include <mach/mach.h>
#endif

// Use clock_gettime in linux, clock_get_time in OS X.
void get_monotonic_time(struct timespec *ts){
#ifdef __MACH__
  clock_serv_t cclock;
  mach_timespec_t mts;
  host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock);
  clock_get_time(cclock, &mts);
  mach_port_deallocate(mach_task_self(), cclock);
  ts->tv_sec = mts.tv_sec;
  ts->tv_nsec = mts.tv_nsec;
#else
  clock_gettime(CLOCK_MONOTONIC, ts);
#endif
}

double get_elapsed_time(struct timespec *before, struct timespec *after){
  double deltat_s  = after->tv_sec - before->tv_sec;
  double deltat_ns = after->tv_nsec - before->tv_nsec;
  return deltat_s + deltat_ns*1e-9;
}

int main(){

  // Do something and time how long it takes.
  struct timespec before, after;
  get_monotonic_time(&before);
  double sum=0.;
  unsigned u;
  for(u=1; u<100000000; u++)
    sum += 1./u/u;
  get_monotonic_time(&after);
  printf("sum = %e\n", sum);
  printf("deltaT = %e s\n", get_elapsed_time(&before,&after));

}

猜测一下,如果你正在使用高精度计时器,你可能关心准确性或性能。每次想要获取时间然后再释放端口来查找mach服务都没有帮助。只需执行一次! - marko
@marko 是的,如果经常调用这些函数,那就是真的。在我的情况下,我只想在一些昂贵的函数调用之前和之后调用这些计时函数几次,所以这不是问题。 - Douglas B. Staple
看起来你可以使用mach_absolute_time代替对host_get_clock_serviceclock_get_timemach_port_deallocate的调用。 - JWWalker

0

我使用了Douglas的答案(被接受的答案),他的参考资料以及其他在互联网上流传的示例(例如this question)。

这个答案包括了我的版本的代码,它模拟了CLOCK_REALTIMECLOCK_MONOTONICclock_gettime函数。它还模拟了绝对单调时间的clock_nanosleep()函数。该代码托管在GitHub 这里

为了使其工作,你的代码中唯一需要的额外部分是

#ifdef __MACH__
timing_mach_init();
#endif

然后,您可以像使用实际符合 POSIX 规范(具有实时扩展)的系统一样使用 clock_gettime()clock_nanosleep_abstime()


如果你想清理不再需要的计时器,可以在源文件中创建一个函数,调用mach_port_deallocate(mach_host_self(), timing_mach_g.cclock) - ChisholmKyle

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