问题描述
我花了很多时间来测量给定指令的精确时钟周期,部分代码是用 C 编写的。但是,我永远无法准确测量运行时需要多少个周期,我使用了 PAPI,“rtdsc”硬件计数器,时钟(),带有“CLOCK_REALTIME”的clock_gettime。然而,对于每次运行,我得到不同的数字,(我不想将程序迭代超过 100000 次并获得平均值,在我的情况下不起作用!即使有效,平均值仍然是在我看来不是确切的方式),
我很惊讶没有精确的测量工具,因为我们确切地知道有多少指令,我们怎么可能没有合适的测量工具!
我用 oprofile 和 vtune(里面使用了 pref)进行了分析,我确切地知道发生了什么,但我如何才能准确地测量它?恐怕没有办法!
对于下面的示例代码,调用斐波那契函数,没办法得到准确的周期数!
#include <stdio.h>
#include <time.h>
#include <math.h>
#include <stdint.h>
#ifdef PAPI
#include <papi.h>
#endif
uint64_t fib(int n) { return n < 2 ? (uint64_t)n : fib(n-1) + fib(n-2); }
#define rdtscll(val) { \
unsigned int __a,__d; \
asm volatile("rdtsc" : "=a" (__a),"=d" (__d)); \
(val) = ((unsigned long)__a) | (((unsigned long)__d)<<32); \
}
int main(int argc,char **argv)
{
struct timespec tv;
long long start,stop;
#ifdef PAPI
// gcc test.c -I/${PAPI_DIR}/include -L/${PAPI_DIR}/lib -O3 -o test -lpapi
if (PAPI_library_init(PAPI_VER_CURRENT) != PAPI_VER_CURRENT)
exit(1);
start = PAPI_get_real_cyc();
#ifdef math
fib(10);
#endif
stop = PAPI_get_real_cyc();
printf("total cycles : %lld\n",stop-start);
#endif
#ifdef clk
start = clock();
#ifdef math
fib(10);
#endif
stop = clock();
printf("total cycles : %lld\n",stop-start);
#endif
#ifdef Wtime
clock_gettime(CLOCK_REALTIME,&tv);
start= (tv.tv_sec) * 1000000000 + (tv.tv_nsec);
#ifdef math
fib(10);
#endif
clock_gettime(CLOCK_REALTIME,&tv);
stop= (tv.tv_sec) * 1000000000 + (tv.tv_nsec);
printf("total time : %lld\n",stop-start);
#endif
#ifdef hardware_counter
rdtscll(start);
#ifdef math
fib(10);
#endif
rdtscll(stop);
printf("total cycles : %lld\n",stop-start);
#endif
return 0;
}
例如,我将其中一些的 objdump 输出放在这里 rtdsc:
0000000000400450 <main>:
400450: 48 83 ec 08 sub $0x8,%rsp
400454: 0f 31 rdtsc
400456: 89 c0 mov %eax,%eax
400458: 48 c1 e2 20 shl $0x20,%rdx
40045c: 48 09 c2 or %rax,%rdx
40045f: b8 09 00 00 00 mov $0x9,%eax
400464: 49 89 d3 mov %rdx,%r11
400467: 83 f8 01 cmp $0x1,%eax
40046a: 7e 5c jle 4004c8 <main+0x78>
40046c: 44 8d 48 fe lea -0x2(%rax),%r9d
400470: 44 8d 50 fd lea -0x3(%rax),%r10d
400474: 44 8d 40 ff lea -0x1(%rax),%r8d
400478: 44 89 c8 mov %r9d,%eax
40047b: 83 e0 fe and $0xfffffffe,%eax
40047e: 41 29 c2 sub %eax,%r10d
400481: 44 89 c7 mov %r8d,%edi
400484: e8 37 01 00 00 callq 4005c0 <xfib>
400489: 41 83 e8 02 sub $0x2,%r8d
40048d: 45 39 d0 cmp %r10d,%r8d
400490: 75 ef jne 400481 <main+0x31>
400492: 41 83 f9 ff cmp $0xffffffff,%r9d
400496: 44 89 c8 mov %r9d,%eax
400499: 75 cc jne 400467 <main+0x17>
40049b: 0f 31 rdtsc
40049d: 89 c0 mov %eax,%eax
40049f: 48 c1 e2 20 shl $0x20,%rdx
4004a3: be 24 0a 40 00 mov $0x400a24,%esi
4004a8: 48 09 c2 or %rax,%rdx
4004ab: bf 01 00 00 00 mov $0x1,%edi
4004b0: 31 c0 xor %eax,%eax
4004b2: 4c 29 da sub %r11,%rdx
4004b5: e8 76 ff ff ff callq 400430 <__printf_chk@plt>
4004ba: 31 c0 xor %eax,%eax
4004bc: 48 83 c4 08 add $0x8,%rsp
4004c0: c3 retq
4004c1: 0f 1f 80 00 00 00 00 nopl 0x0(%rax)
4004c8: 44 8d 48 fe lea -0x2(%rax),%r9d
4004cc: 41 83 f9 ff cmp $0xffffffff,%r9d
4004d0: 44 89 c8 mov %r9d,%eax
4004d3: 75 92 jne 400467 <main+0x17>
4004d5: eb c4 jmp 40049b <main+0x4b>
4004d7: 66 0f 1f 84 00 00 00 nopw 0x0(%rax,%rax,1)
4004de: 00 00
在不调用另一个库、标准输出等的情况下,有什么方法可以准确和一致地测量计算机在一部分代码之间使用的时钟周期数?
感谢您的帮助,
编辑: 感谢大家的评论,关于框架,我在运行 ubuntu 16.04 和 ubuntu 18.04 的物理机上使用 x86_64 和 aarch64。在 aarch64 上的 PAPI 情况下无法通过验证测试,所以我跳过了 PAPI。但是,我也使用“perf”尝试了这个主题,但结果相同, 性能循环计数器 -> https://stackoverflow.com/a/64898073/3101659
我认为这与计算机体系结构的基础有关,在冯诺依曼机器中,使用“随机内存访问”、“brach 预测”和其他东西是我们无法在我们的程序中预测和实现相同周期的原因. 如果我在 FPGA 设备上使用 HLS 运行此程序,我可以准确地测量设备逐个周期使用的周期数,而在基于冯诺依曼的机器等传统机器上,这是不可能的。
解决方法
暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!
如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。
小编邮箱:dio#foxmail.com (将#修改为@)