如何确定在C程序中执行的x86机器指令的数量?

K. *_*man 4 c x86 profiling isa

我正在做一个家庭作业问题,要求我找出运行我在C中写的短程序时执行的机器代码指令的数量.

问题是我可以使用我想要的任何工具来解决它,但我对C很新,并且很少知道如何解决这个问题.

我需要哪些类型的工具来解决这个问题?

Pet*_*des 11

术语:您要求的是动态指令计数.例如,每次执行时计算循环内的指令.这通常与性能大致相关,但每周期指令可能会有很大差异.

人们也看到的是静态指令计数(或者通常只是代码大小,因为这对于指令缓存占用和磁盘加载时间来说真正重要).对于像x86这样的可变长度指令集,它们是相关的,但不是相同的.在具有固定长度指令的RISC上,例如MIPS或AArch64,它更接近,但您仍然可以使用填充来对齐函数的开始.这是一个完全独立的指标. gcc -Os优化代码大小,同时尽量不牺牲速度.


如果您使用的是Linux,请使用gcc -O2 foo.c编译代码. -O2不为gcc启用自动矢量化.(它适用于铿锵声).它可能是一个很好的基线优化级别,可以消除实际上不需要发生的C代码中的内容,避免使用更多或更少的tmp变量来分解大表达式之间的愚蠢差异.-Og如果你想要最小化优化,或者-O0如果你想要真正愚蠢的脑code代码分别编译每个语句并且从不在语句之间保留任何东西,也许可以使用.(为什么clang用-O0产生效率低的asm(对于这个简单的浮点和)?).

是的,这是相当重要的巨大如何编译. gcc -O3 -march=native -ffast-math如果它自动矢量化循环,可能会使用更少的指令.

要阻止代码优化,请从命令行arg获取输入,或从volatile变量中读取它.喜欢volatile int size_volatile = 1234; int size = size_volatile;.返回或打印结果,因为如果程序没有副作用,那么最有效的实现就是立即退出.


然后跑perf stat ./a.out.这将使用硬件性能计数器为您提供代表您的进程执行的完整指令,包括内核内部.(与其他计数器一起,如CPU核心时钟周期,以及一些软件计数器page-faults和时间,以微秒为单位.)

要仅计算用户空间指令,请使用perf stat -e instructions:u ./a.out.即使对于简单的"hello world"程序(如180k),这仍然是一个非常大的数字,因为这包括动态链接器启动和在库函数内运行的所有代码.和CRT启动代码调用你的main,并exit使用main返回值进行系统调用,如果你返回而不是调用exit(3).

您可以静态链接您的C程序,通过编译来减少启动开销 gcc -O2 -static -fno-stack-protector -fno-pie -no-pie

perfinstructions:u在我的Skylake CPU上计数似乎相当准确.静态链接的x86-64二进制文件仅包含2条指令mov eax, 231/ syscall,计为3条指令.可能在内核和用户模式之间的转换中有一个额外的指令被计算,但这是非常小的.

$ perf stat -e instructions:u ./exit    # hand-written in asm to check for perf overhead

 Performance counter stats for './exit':

                 3      instructions:u                                              

       0.000651529 seconds time elapsed
Run Code Online (Sandbox Code Playgroud)

一个静态链接的二进制文件,调用puts两次计数33,202 instructions:u,用gcc -O2 -static -fno-stack-protector -fno-pie -no-pie hello.c.编译.在调用之前,glibc init函数(包括stdio和CRT启动函数)似乎是合理的main.(main本身只有8条指令,我查过objdump -drwC -Mintel a.out | less).


其他资源:


二进制仪器工具:

这些是计算指令的重要工具,包括仅计算特定类型的指令.

  • 英特尔引脚 - 动态二进制仪表工具
  • 英特尔®软件开发仿真器(SDE) 这是基于PIN的,对于在不支持AVX512的开发机器上测试AVX512代码这样的事情非常方便.(它动态地重新编译,因此大多数指令本机运行,但不支持的指令调用仿真例程.)

    例如,sde64 -mix -- ./my_program将为您的程序打印指令组合,每个不同指令的总计数和类别细分.有关输出类型的示例,请参阅使用AVX编译的libsvm与无AVX编译.

    它还为您提供了每个函数的总动态指令计数表,以及每线程和全局表. 但是,SDE混合输出在PIE可执行文件上不能正常工作:它认为动态链接器是可执行文件(因为它是),所以编译时使用gcc -O2 -no-pie -fno-pie prog.c -o prog.尽管如此,它仍然没有在概要输出中看到puts调用或者main它自己的测试程序,我不知道为什么不.

  • 使用英特尔®软件开发仿真器(英特尔®SDE)计算"FLOP"使用 SDE计算某些类型指令的示例,例如vfmadd231pd.

    Intel CPU具有用于事件的HW perf计数器fp_arith_inst_retired.256b_packed_double,因此您可以使用它们来计算FLOP.他们实际上将FMA视为2个事件.因此,如果您的英特尔CPU可以本机运行您的代码,那么您可以使用perf stat -e -e fp_arith_inst_retired.256b_packed_double,fp_arith_inst_retired.128b_packed_double,fp_arith_inst_retired.scalar_double.(和/或单精度事件.)

    但是大多数其他特定类型的指令都没有事件,只有FP数学.

这是英特尔的全部内容; IDK是什么,或者除了x86之外的任何ISA内容.这些只是我听说过的工具; 我敢肯定有很多东西我要遗漏了.


归档时间:

查看次数:

579 次

最近记录:

7 年,2 月 前