快速计算C程序中执行的指令数

时间:2012-11-09 18:02:57

标签: c linux profile

在执行C程序时,是否有一种简单的方法可以快速计算执行的指令数量(x86指令 - 每个指令的数量和数量)?

我在gcc version 4.7.1 (GCC)计算机上使用x86_64 GNU/Linux

5 个答案:

答案 0 :(得分:3)

可能是this question

的副本

我说可能是因为您要求汇编程序指令,但该问题处理代码的C级分析。

然而,我的问题是:为什么要描述执行的实际机器指令?作为第一个问题,各种编译器和它们的优化设置之间会有所不同。作为一个更实际的问题,你真的可以用这些信息做些什么?如果您正在搜索/优化瓶颈,那么代码分析器就是您正在寻找的。

我可能会错过这里重要的事情。

答案 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__块(subjne)中每个循环有两个指令。

如果我改变自变量,甚至降低到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计数指令数:

  1. 从此处下载最新的(或3.10,如果答案不正确)针包。
  2. 提取所有内容并转到目录:cd pin-root/source/tools/ManualExample/
  3. 制作目录中的所有工具:make all
  4. 使用以下命令运行名为inscount0.so的工具:../../../pin -t obj-intel64/inscount0.so -- your-binary-here
  5. 在文件inscount.outcat 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