在执行C程序时,是否有一种简单的方法可以快速计算执行的指令数量(x86指令 - 每个指令的数量和数量)?
我在gcc version 4.7.1 (GCC)
计算机上使用x86_64 GNU/Linux
。
答案 0 :(得分:3)
我说可能是因为您要求汇编程序指令,但该问题处理代码的C级分析。
然而,我的问题是:为什么要描述执行的实际机器指令?作为第一个问题,各种编译器和它们的优化设置之间会有所不同。作为一个更实际的问题,你真的可以用这些信息做些什么?如果您正在搜索/优化瓶颈,那么代码分析器就是您正在寻找的。 p>
我可能会错过这里重要的事情。
答案 1 :(得分:2)
使用perf_event_open
的Linux config = PERF_COUNT_HW_INSTRUCTIONS
系统调用
此Linux系统调用似乎是性能事件的跨体系结构包装。
这是从man perf_event_open
页改编而成的示例:
perf_event_open.c
#include <asm/unistd.h>
#include <linux/perf_event.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <inttypes.h>
static long
perf_event_open(struct perf_event_attr *hw_event, pid_t pid,
int cpu, int group_fd, unsigned long flags)
{
int ret;
ret = syscall(__NR_perf_event_open, hw_event, pid, cpu,
group_fd, flags);
return ret;
}
int
main(int argc, char **argv)
{
struct perf_event_attr pe;
long long count;
int fd;
uint64_t n;
if (argc > 1) {
n = strtoll(argv[1], NULL, 0);
} else {
n = 10000;
}
memset(&pe, 0, sizeof(struct perf_event_attr));
pe.type = PERF_TYPE_HARDWARE;
pe.size = sizeof(struct perf_event_attr);
pe.config = PERF_COUNT_HW_INSTRUCTIONS;
pe.disabled = 1;
pe.exclude_kernel = 1;
// Don't count hypervisor events.
pe.exclude_hv = 1;
fd = perf_event_open(&pe, 0, -1, -1, 0);
if (fd == -1) {
fprintf(stderr, "Error opening leader %llx\n", pe.config);
exit(EXIT_FAILURE);
}
ioctl(fd, PERF_EVENT_IOC_RESET, 0);
ioctl(fd, PERF_EVENT_IOC_ENABLE, 0);
/* Loop n times, should be good enough for -O0. */
__asm__ (
"1:;\n"
"sub $1, %[n];\n"
"jne 1b;\n"
: [n] "+r" (n)
:
:
);
ioctl(fd, PERF_EVENT_IOC_DISABLE, 0);
read(fd, &count, sizeof(long long));
printf("Used %lld instructions\n", count);
close(fd);
}
编译并运行:
g++ -ggdb3 -O0 -std=c++11 -Wall -Wextra -pedantic -o perf_event_open.out perf_event_open.c
./perf_event_open.out
输出:
Used 20016 instructions
因此,我们看到结果非常接近20000的预期值:10k * __asm__
块(sub
,jne
)中每个循环有两个指令。
如果我改变自变量,甚至降低到100
之类的低值:
./perf_event_open.out 100
它给出:
Used 216 instructions
维护该常数+ 16条指令,所以看起来精度非常高,这16条必须只是我们的小循环后的ioctl
设置指令。
现在您可能也对以下内容感兴趣:
此系统调用可以衡量的其他感兴趣的事件:
已在Ubuntu 20.04 amd64,GCC 9.3.0,Linux内核5.4.0,Intel Core i7-7820HQ CPU上进行了测试。
答案 2 :(得分:1)
您可以使用硬件性能计数器(HPC)轻松计算执行指令的数量。要访问HPC,您需要一个接口。我建议你使用PAPI Performance API。
答案 3 :(得分:0)
尽管根据程序的不同,它不是“快速”的,但可能已经in this question得到了回答。在这里,Mark Plotnick建议使用gdb
来监视程序计数器寄存器的更改:
# instructioncount.gdb
set pagination off
set $count=0
while ($pc != 0xyourstoppingaddress)
stepi
set $count++
end
print $count
quit
然后,在您的程序上启动gdb
:
gdb --batch --command instructioncount.gdb --args ./yourexecutable with its arguments
要获取结束地址0xyourstoppingaddress
,可以使用以下脚本:
# stopaddress.gdb
break main
run
info frame
quit
在函数main
上设置一个断点,并给出:
$ gdb --batch --command stopaddress.gdb --args ./yourexecutable with its arguments
...
Stack level 0, frame at 0x7fffffffdf70:
rip = 0x40089d in main (main_aes.c:33); saved rip 0x7ffff7a66d20
source language c.
Arglist at 0x7fffffffdf60, args: argc=3, argv=0x7fffffffe048
...
在这里重要的是saved rip 0x7ffff7a66d20
部分。在我的CPU上,rip
是指令指针,saved rip
是“返回地址”,如pepero in this answer所述。
因此,在这种情况下,停止地址为0x7ffff7a66d20
,它是main
函数的返回地址。也就是说,程序执行结束。
答案 4 :(得分:0)
您可以使用Intel的二进制工具'Pin'。我会避免使用模拟器来完成这样的琐碎任务(模拟器通常非常慢)。 Pin可以完成您可以使用模拟器完成的大部分工作,而无需预先修改二进制文件,并且可以像速度一样正常执行(取决于您使用的Pin工具)。
要用Pin计数指令数:
cd pin-root/source/tools/ManualExample/
make all
../../../pin -t obj-intel64/inscount0.so -- your-binary-here
inscount.out
,cat inscount.out
中获取指令计数。输出类似于:
➜ ../../../pin -t obj-intel64/inscount0.so -- /bin/ls
buffer_linux.cpp itrace.cpp
buffer_windows.cpp little_malloc.c
countreps.cpp makefile
detach.cpp makefile.rules
divide_by_zero_unix.c malloc_mt.cpp
isampling.cpp w_malloctrace.cpp
➜ cat inscount.out
Count 716372