用C程序计算C代码执行了多少条机器指令(仅支持部分Linux系统)【视频介绍】

!!!喜欢看视频的朋友请点这里!!!

一、来源

最近学习时间复杂度的时候灵机一动,觉得除了运行时间之外,能不能通过指令数来比较两个算法的性能呢?
自己不会写,于是去网上找了一圈,目前最接近我想像中的结果的是stackoverflow上的一个答案:Quick way to count number of instructions executed in a C program


二、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>
#include <sys/types.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;
  pe.exclude_hv = 1; // Don't count hypervisor events.
  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);

  /* ---------------------- 以下是需要测试的业务代码 ---------------------- */
  for (int i = 0; i < 10000; ++i);
  /* ---------------------- 以上是需要测试的业务代码 ---------------------- */

  ioctl(fd, PERF_EVENT_IOC_DISABLE, 0);
  read(fd, &count, sizeof(long long));
  printf("Used %lld instructions\n", count);
  close(fd);
}

说明:

  1. 要测试的业务代码在43行,本例中是测试for循环1000次要执行的指令数。其他的代码我基本都看不懂,建议你也不要改。
  2. 程序依赖了Linux环境,不能在Windows下执行。

三、编译运行

跟普通的C程序的编译运行没有区别,假设文件名是perf_event_open.c,则编译并运行的命令如下:

gcc perf_event_open.c -o perf_event_open.out  && ./perf_event_open.out

运行正常时应该会打印类型如下信息:

Used 30018 instructions

运行不正常时可能会打印如下信息:

Error opening leader 1

四、Error opening leader问题

如果你直接就能运行成功,恭喜你,这一小节可以不用看了。
如果你运行失败,出现了Error opening leader 1的提示,则可能的原因有两种:

  1. 你是在vmware虚拟机上运行,但是虚拟机的设置不正确。
  2. 你的CPU不支持运行本程序。

4.1 vmware虚拟机设置

如果你是在vmware虚拟机上运行,需要开启虚拟化 CPU 性能计数器(U)的功能。
菜单:编辑虚拟机配置(需要先关闭虚拟机) -> 硬件标签页 -> 处理器 -> 勾选虚拟化 CPU 性能计数器(U)
在这里插入图片描述
在这里插入图片描述
然后启动虚拟机就可以了。


4.2 CPU不支持本程序

本程序中使用了CPU提供的PMU(Performance Monitoring Units)功能,即性能监控单元。如果你的CPU不支持PMU,这种情况就没有办法了。
怎么确定是否支持呢?这个我没找到相关的文章,因此没有准确的答案,但是两个有个猜测的答案。


4.2.1 第一种方法:使用dmesg命令

执行以下命令:

 dmesg | grep PMU

如果支持,则会得到类似如下的结果:
在这里插入图片描述
如果不支持,则什么都不会打印。


4.2.1 第二种方法:使用perf命令

如果不存在perf命令,则需要先安装。
Centos8安装perf:

yum install -y perf

Ubuntu20.04安装perf:

apt install -y linux-tools-common linux-tools-generic linux-tools-5.11.0-44-generic linux-cloud-tools-5.11.0-44-generic

安装完之后,执行以下命令:

perf list pmu

如果支持PMU,得到的结果类似这样(其中包含 instructions 就是跟指令相关的):

List of pre-defined events (to be used in -e):

  branch-instructions OR cpu/branch-instructions/    [Kernel PMU event]
  branch-misses OR cpu/branch-misses/                [Kernel PMU event]
  bus-cycles OR cpu/bus-cycles/                      [Kernel PMU event]
  cache-misses OR cpu/cache-misses/                  [Kernel PMU event]
  cache-references OR cpu/cache-references/          [Kernel PMU event]
  cpu-cycles OR cpu/cpu-cycles/                      [Kernel PMU event]
  instructions OR cpu/instructions/                  [Kernel PMU event]
  ref-cycles OR cpu/ref-cycles/                      [Kernel PMU event]
  topdown-fetch-bubbles OR cpu/topdown-fetch-bubbles/ [Kernel PMU event]
  topdown-recovery-bubbles OR cpu/topdown-recovery-bubbles/ [Kernel PMU event]
  topdown-slots-issued OR cpu/topdown-slots-issued/  [Kernel PMU event]
  topdown-slots-retired OR cpu/topdown-slots-retired/ [Kernel PMU event]
  topdown-total-slots OR cpu/topdown-total-slots/    [Kernel PMU event]
  msr/pperf/                                         [Kernel PMU event]
  msr/smi/                                           [Kernel PMU event]
  msr/tsc/                                           [Kernel PMU event]

如果不支持,得到的结果类似这样:

List of pre-defined events (to be used in -e):

  ref-cycles OR cpu/ref-cycles/                      [Kernel PMU event]
  topdown-fetch-bubbles OR cpu/topdown-fetch-bubbles/ [Kernel PMU event]
  topdown-recovery-bubbles OR cpu/topdown-recovery-bubbles/ [Kernel PMU event]
  topdown-slots-issued OR cpu/topdown-slots-issued/  [Kernel PMU event]
  topdown-slots-retired OR cpu/topdown-slots-retired/ [Kernel PMU event]
  topdown-total-slots OR cpu/topdown-total-slots/    [Kernel PMU event]
  msr/pperf/                                         [Kernel PMU event]
  msr/smi/                                           [Kernel PMU event]
  msr/tsc/                                           [Kernel PMU event]

五、误差说明

在上面的C语言代码中,把第43行的业务代码注释掉,保存、编译、运行,你会发现指令数量并不为0,在我的Ubuntu20.24上是14,在我的Centos8上是13,你自己的环境可能也会得到不同的值,这个值就是误差值,因此你实际计算时需要减掉这个值。


全文完

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值