ARM性能计数器与Linux的clock_gettime

我在开发板(ZC702)上使用了一个Zynq芯片,该芯片有一个667MHz的双Cortex-A9 MPCore,并带有一个Linux内核3.3。我想比较一个程序的执行时间,所以首先使用clock_gettime,然后使用计数器由ARM的协处理器提供。 计数器每增加一个处理器周期。 (基于这个问题的stackoverflow和这个 )

我用-O0标志编译程序(因为我不想要重新sorting或优化)

我用性能计数器测量的时间是583833498(周期)/ 666.666687 MHz = 875750.221 (微秒)

在使用clock_gettime()(REALTIME或MONOTONIC或MONOTONIC_RAW )时,测量的时间为: 731627.126 (微秒),这个时间less了150000微秒。

有谁能解释我为什么会这样呢? 为什么有差异? 处理器没有时钟规模,如何通过clock_gettime获得更less的执行时间? 我有一个示例代码如下:


#define RUNS 50000000 #define BENCHMARK(val) \ __asm__ __volatile__("mov r4, %1\n\t" \ "mov r5, #0\n\t" \ "1:\n\t"\ "add r5,r5,r4\n\t"\ "mov r4 ,r4 \n\t" \ "mov r4 ,r4 \n\t" \ "mov r4 ,r4 \n\t" \ "mov r4 ,r4 \n\t" \ "mov r4 ,r4 \n\t" \ "mov r4 ,r4 \n\t" \ "mov r4 ,r4 \n\t" \ "mov r4 ,r4 \n\t" \ "mov r4 ,r4 \n\t" \ "mov r4 ,r4 \n\t" \ "sub r4,r4,#1\n\t" \ "cmp r4, #0\n\t" \ "bne 1b\n\t" \ "mov %0 ,r5 \n\t" \ :"=r" (val) \ : "r" (RUNS) \ : "r4","r5" \ ); clock_gettime(CLOCK_MONOTONIC_RAW,&start); __asm__ __volatile__ ("MRC p15, 0, %0, c9, c13, 0\t\n": "=r"(start_cycles)); for(index=0;index<5;index++) { BENCHMARK(i); } __asm__ __volatile__ ("MRC p15, 0, %0, c9, c13, 0\t\n": "=r"(end_cycles)); clock_gettime(CLOCK_MONOTONIC_RAW,&stop); 

我找到了解决方案。 我将该平台从3.3.0版本的Linux内核升级到3.5版本,其值与性能计数器相似。 显然3.3.0中的时钟计数器的频率假设高于(400MHz左右),而不是CPU频率的一半。 可能是旧版本的移植错误。

POSIX时钟在一定的精度内运行,你可以用clock_getres获得。 检查150,000us的差异是在误差范围之内还是之外。

无论如何,不​​要紧,你应该多次重复你的基准,不是5,而是1000或更多。 然后你可以得到一个单一的基准测试运行的时间

((end + e1) - (start + e0)) / 1000 ,或者

(end - start) / 1000 + (e1 - e0) / 1000

如果e1e0是误差项,它们被一个很小的常数限制,那么最大测量误差将是abs (e1 - e0) / 1000 ,随着回路数量的增加,这个误差可以忽略不计。