clock_gettime()是否适合亚微秒时序?

Cra*_*rks 20 linux ubuntu performance profiling

在我们的应用程序的Linux版本中,我需要一个用于嵌入式探查器的高分辨率计时器.我们的分析器测量的范围与单个功能一样小,因此它需要一个优于25纳秒的定时器精度.

以前我们的实现使用内联汇编和rdtsc操作直接从CPU查询高频定时器,但这是有问题的,需要经常重新校准.

所以我尝试使用该clock_gettime函数来查询CLOCK_PROCESS_CPUTIME_ID.文档声称这给了我纳秒时间,但我发现单次调用的开销clock_gettime()超过250ns.这使得不可能将事件计时100ns,并且在计时器功能上具有如此高的开销会严重降低应用程序性能,从而扭曲配置文件超出值.(我们每秒有数十万个分析节点.)

有没有办法调用clock_gettime()开销小于¼μs?或者是否有其他方法可以可靠地获得时间戳计数器,开销<25ns?还是我坚持使用rdtsc

下面是我过去常用的代码clock_gettime().

// calls gettimeofday() to return wall-clock time in seconds:
extern double Get_FloatTime();
enum { TESTRUNS = 1024*1024*4 };

// time the high-frequency timer against the wall clock
{
    double fa = Get_FloatTime();
    timespec spec; 
    clock_getres( CLOCK_PROCESS_CPUTIME_ID, &spec );
    printf("CLOCK_PROCESS_CPUTIME_ID resolution: %ld sec %ld nano\n", 
            spec.tv_sec, spec.tv_nsec );
    for ( int i = 0 ; i < TESTRUNS ; ++ i )
    {
        clock_gettime( CLOCK_PROCESS_CPUTIME_ID, &spec );
    }
    double fb = Get_FloatTime();
    printf( "clock_gettime %d iterations : %.6f msec %.3f microsec / call\n",
        TESTRUNS, ( fb - fa ) * 1000.0, (( fb - fa ) * 1000000.0) / TESTRUNS );
}
// and so on for CLOCK_MONOTONIC, CLOCK_REALTIME, CLOCK_THREAD_CPUTIME_ID.
Run Code Online (Sandbox Code Playgroud)

结果:

CLOCK_PROCESS_CPUTIME_ID resolution: 0 sec 1 nano
clock_gettime 8388608 iterations : 3115.784947 msec 0.371 microsec / call
CLOCK_MONOTONIC resolution: 0 sec 1 nano
clock_gettime 8388608 iterations : 2505.122119 msec 0.299 microsec / call
CLOCK_REALTIME resolution: 0 sec 1 nano
clock_gettime 8388608 iterations : 2456.186031 msec 0.293 microsec / call
CLOCK_THREAD_CPUTIME_ID resolution: 0 sec 1 nano
clock_gettime 8388608 iterations : 2956.633930 msec 0.352 microsec / call
Run Code Online (Sandbox Code Playgroud)

这是在标准的Ubuntu内核上.该应用程序是Windows应用程序的端口(我们的rdtsc内联汇编工作正常).

附录:

x86-64 GCC是否有一些与__rdtsc()的内在等价物,所以我至少可以避免内联汇编?

Dav*_*rtz 7

不可以.您必须使用特定于平台的代码才能执行此操作.在x86和x86-64上,您可以使用'rdtsc'来读取时间戳计数器.

只需移植您正在使用的rdtsc程序集.

__inline__ uint64_t rdtsc(void) {
  uint32_t lo, hi;
  __asm__ __volatile__ (      // serialize
  "xorl %%eax,%%eax \n        cpuid"
  ::: "%rax", "%rbx", "%rcx", "%rdx");
  /* We cannot use "=A", since this would use %rax on x86_64 and return only the lower 32bits of the TSC */
  __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi));
  return (uint64_t)hi << 32 | lo;
}
Run Code Online (Sandbox Code Playgroud)

  • 这些问题已经过时了六年,现在已成为历史垃圾箱的一部分.如果没有恒定的TSC,您不太可能看到现代服务器. (5认同)
  • 如果你检查/ proc/cpuinfo,你应该在每个现代CPU上看到'constant_tsc'.如果TSC不是跨核心同步的,或者它们应该是不恒定的,那就是一个bug.(你应该报告.) (5认同)
  • @David你有一些消息来支持吗?在过去几年中,与多CPU同步和时钟节流功耗相关的时序问题有何变化? (2认同)
  • 我同意@DavidSchwartz:例如我的沙桥箱:我看到`flags:fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl pni monitor ds_cpl vmx smx est tm2 ssse3 cx16 xtpr dca sse4_1 sse4_2 x2apic popcnt lahf_lm ida` constant_tsc set.您也可以参考http://download.intel.com/design/processor/manuals/253668.pdf第16.12.1节"不变TSC" (2认同)

Chr*_*Wue 6

我在我的系统上运行了一些基准测试,这是一个四核E5645 Xeon支持一个运行内核3.2.54的恒定TSC,结果如下:

clock_gettime(CLOCK_MONOTONIC_RAW)       100ns/call
clock_gettime(CLOCK_MONOTONIC)           25ns/call
clock_gettime(CLOCK_REALTIME)            25ns/call
clock_gettime(CLOCK_PROCESS_CPUTIME_ID)  400ns/call
rdtsc (implementation @DavidSchwarz)     600ns/call
Run Code Online (Sandbox Code Playgroud)

所以看起来在一个相当现代的系统上(接受的答案)rdtsc是最糟糕的路线.