上一次讲了DVFS,但是论文中都是根据PMC计算功耗。仿真中PMC容易获得,但是实际的系统中,我们很难获得,得从linux内核源码层次访问寄存器。

简单的我们可以使用perf_event_open()这个子系统来获得,虽然不太方便吧,还是可以用的。

大致上讲,perf_event_open()有两个使用模式,一个叫做计数,一个叫做采样。所谓计数,就是测量一段时间内某个事件发生的次数,比如获取每1秒内运行的指令数。所谓采样,就是在某个时间点查看某个状态,比如我想每1秒记录下当前的IP寄存器的值。从编程的角度看,计数模式要容易理解地多,也容易实现地多。所以这篇博客先讲怎么使用perf的计数模式。

最简单的计数模式就是只监测一个计数器。比如我每一秒获取刚刚过去的那一秒内的指令数(instructions)。复杂的计数模式就是同时监测多个计数器。比如我每一秒获取刚刚过去的那一秒内的指令数(instructions)、时钟周期数(cycles)、分支指令数(branch instructions)等等。接下来就先讲单一计数器模式,而后讲多计数器模式。

举一个简单的例子:

#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <linux/perf_event.h>//目前perf_event_open在glibc中没有封装,需要手工封装一下
int perf_event_open(struct perf_event_attr *attr,pid_t pid,int cpu,int group_fd,unsigned long flags)
{return syscall(__NR_perf_event_open,attr,pid,cpu,group_fd,flags);
}int main()
{struct perf_event_attr attr;memset(&attr,0,sizeof(struct perf_event_attr));attr.size=sizeof(struct perf_event_attr);//监测硬件attr.type=PERF_TYPE_HARDWARE;//监测指令数attr.config=PERF_COUNT_HW_INSTRUCTIONS;//初始状态为禁用attr.disabled=1;//创建perf文件描述符,其中pid=0,cpu=-1表示监测当前进程,不论运行在那个cpu上int fd=perf_event_open(&attr,0,-1,-1,0);if(fd<0){perror("Cannot open perf fd!");return 1;}//启用(开始计数)ioctl(fd,PERF_EVENT_IOC_ENABLE,0);while(1){uint64_t instructions;//读取最新的计数值read(fd,&instructions,sizeof(instructions));printf("instructions=%ld\n",instructions);sleep(1);}
}

在编译后可以直接运行,会输出instructions值。如果需要得到每秒的值,除了得到的数据相减之外,我们还可以每次将计数器清0。代码如下

#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <linux/perf_event.h>//目前perf_event_open在glibc中没有封装,需要手工封装一下
int perf_event_open(struct perf_event_attr *attr,pid_t pid,int cpu,int group_fd,unsigned long flags)
{return syscall(__NR_perf_event_open,attr,pid,cpu,group_fd,flags);
}int main()
{struct perf_event_attr attr;memset(&attr,0,sizeof(struct perf_event_attr));attr.size=sizeof(struct perf_event_attr);//监测硬件attr.type=PERF_TYPE_HARDWARE;//监测指令数attr.config=PERF_COUNT_HW_INSTRUCTIONS;//初始状态为禁用attr.disabled=1;//创建perf文件描述符,其中pid=0,cpu=-1表示监测当前进程,不论运行在那个cpu上int fd=perf_event_open(&attr,0,-1,-1,0);if(fd<0){perror("Cannot open perf fd!");return 1;}//启用(开始计数)ioctl(fd,PERF_EVENT_IOC_ENABLE,0);while(1){uint64_t instructions;//读取最新的计数值read(fd,&instructions,sizeof(instructions));//读取后清零ioctl(fd,PERF_EVENT_IOC_RESET,0);printf("instructions=%ld\n",instructions);sleep(1);}
}

除了监控指令数,还有很多可以,具体可以看官方手册

如果你说多个计数器简单,创建多个perf文件描述符,然后每个都用read去读嘛。额,确实可以!但是呢,当监测的事件很多,而且读取频率很高时,那么read()调用的开销就不可再忽略了。那么如果能够把多个计数器的值通过一次read()调用获取,性能上能够提高不少。perf提供了“组”的概念,这也是多计数器perf的核心内容。第一个perf fd还是和本来一样的方法创建(除了要多设置一个read_format),而后面的perf_event_open()中的参数group_fd就传入第一个perf fd。这样他们就成为了一个组,并且用第一个perf fd代表整个组。下面代码演示了如何同时监测指令数和时钟周期数。

#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <linux/perf_event.h>//目前perf_event_open在glibc中没有封装,需要手工封装一下
int perf_event_open(struct perf_event_attr *attr,pid_t pid,int cpu,int group_fd,unsigned long flags)
{return syscall(__NR_perf_event_open,attr,pid,cpu,group_fd,flags);
}//每次read()得到的结构体
struct read_format
{//计数器数量(为2)uint64_t nr;//两个计数器的值uint64_t values[2];
};int main()
{struct perf_event_attr attr;memset(&attr,0,sizeof(struct perf_event_attr));attr.size=sizeof(struct perf_event_attr);//监测硬件attr.type=PERF_TYPE_HARDWARE;//监测指令数attr.config=PERF_COUNT_HW_INSTRUCTIONS;//初始状态为禁用attr.disabled=1;//每次读取一个组attr.read_format=PERF_FORMAT_GROUP;//创建perf文件描述符,其中pid=0,cpu=-1表示监测当前进程,不论运行在那个cpu上int fd=perf_event_open(&attr,0,-1,-1,0);if(fd<0){perror("Cannot open perf fd!");return 1;}//接下来创建第二个计数器memset(&attr,0,sizeof(struct perf_event_attr));attr.size=sizeof(struct perf_event_attr);//监测attr.type=PERF_TYPE_HARDWARE;//监测时钟周期数attr.config=PERF_COUNT_HW_CPU_CYCLES;//初始状态为禁用attr.disabled=1;//创建perf文件描述符int fd2=perf_event_open(&attr,0,-1,fd,0);if(fd2<0){perror("Cannot open perf fd2!");return 1;}//启用(开始计数),注意PERF_IOC_FLAG_GROUP标志ioctl(fd,PERF_EVENT_IOC_ENABLE,PERF_IOC_FLAG_GROUP);while(1){struct read_format aread;//读取最新的计数值,每次读取一个结构体read(fd,&aread,sizeof(struct read_format));printf("instructions=%ld,cycles=%ld\n",aread.values[0],aread.values[1]);sleep(1);}
}

可以注意到,最大的变化就是数据的读取。当使用了“组”的形式之后,那么每次read()就是读取一个特定的结构体。这个结构体struct read_format不是固定的,会根据组内计数器数量和struct perf_event_attr的read_format字段的设置而变化。上面的代码用了最简单的方法,把struct perf_event_attr的read_format设置为PERF_FORMAT_GROUP,那么每次读取的结构体其实就是1+nr个64位整数,其中第一个整数nr就是计数器数量,后面nr个整数就是每一个计数器的值,顺序和加入组的顺序相同。

第二大变化就是ioctl()的第三个参数由0变为了PERF_IOC_FLAG_GROUP。这个标志表明操作是对组进行的,可以理解为kernel帮我们把ioctl依次作用在了组的每一个计数器上。所以呢,如果每次读取后,要把组内所有计数器都清空,需要使用:

ioctl(fd,PERF_EVENT_IOC_RESET,PERF_IOC_FLAG_GROUP);

Linux perf获得性能计数器相关推荐

  1. java火焰_使用linux perf工具生成java程序火焰图

    Java FlameGraph(火焰图)能够非常直观的展示java程序的性能分析结果,方便发现程序热点和进一步调优.本文将展示如何使用linux perf工具生成java程序的火焰图.火焰图大致长这个 ...

  2. 电子书:《Linux Perf Master》

    电子书:<Linux Perf Master> <The Linux Perf Master>(暂用名) 是一本关于开源软件的电子书.本书与常见的专题类书籍不同,作者以应用性能 ...

  3. linux perf 参数,Linux perf命令详解及常用参数解析

    perf是Linux下的一款性能分析工具,能够进行函数级与指令级的热点查找. Perf List 利用perf剖析程序性能时,需要指定当前测试的性能时间.性能事件是指在处理器或操作系统中发生的,可能影 ...

  4. 英特尔cpu支持Linux,英特尔公布对Icelake CPU的Linux Perf支持

    由于英特尔Icelake CPU的核心功能已经到位,英特尔的开源开发人员一直致力于为这些下一代处理器提供硬件支持的其他领域. 我们看到的最新的Icelake Linux补丁被Intel公开,是关于&q ...

  5. linux perf - 性能测试和优化工具

    Perf简介 Perf是Linux kernel自带的系统性能优化工具.虽然它的版本还只是0.0.2,Perf已经显现出它强大的实力,足以与目前Linux流行的OProfile相媲美了. Perf 的 ...

  6. linux perf生成火焰图,火焰图:全局视野的Linux性能剖析

    原标题:火焰图:全局视野的Linux性能剖析 文章背景 日常的工作中,会收到一堆CPU使用率过高的告警邮件,遇到某台服务的 CPU被占满了 ,这时候我们就要去查看是什么进程将服务器的CPU资源占用满了 ...

  7. Linux Perf性能分析常用手段(火焰图,gprof,kernelshark)

    系统级性能优化通常包括两个阶段:性能剖析(performance profiling)和代码优化.性能剖析的目标是寻找性能瓶颈,查找引发性能问题的原因及热点代码.代码优化的目标是针对具体性能问题而优化 ...

  8. Linux perf sched Summary

    1. Overview perf sched 使用了转储后再分析 (dump-and-post-process) 的方式来分析内核调度器的各种事件. 而这往往带来一些问题,因为这些调度事件通常非常地频 ...

  9. linux perf arm,perf arm交叉编译

    前言 perf工具源码位于linux内核目录的tools下 perf工具的编译需要依赖于内核 perf工具必须使用编译linux内核源码的同一个编译器编译 步骤 编译zlib 网上查到资料说需要编译z ...

最新文章

  1. leetcode_linearList03
  2. location.href、location.assign和location.replace的区别
  3. 【Python】全网最新最全Pyecharts可视化教程(三):制作多个子图
  4. 东莞电子计算机学校,东莞市电子科技学校
  5. Shell and powershell
  6. JS中的bind()方法
  7. linux 脚本 if判断 o,shell脚本常用脚本:if判断
  8. ac ap原理、_AP面板是什么?家庭AC+AP的组网方式,真的适合所有人吗?
  9. Django中使用Celery(附赠代码)
  10. 《zabbix中文支持》-4
  11. 【转】如何在命令行脚本中启动带参数的Windows服务
  12. 圈圈教你玩usb第一版件软件使用说明
  13. 雷电2接口_Steinberg 发布旗舰级 32 bit / 384 kHz 雷电 2 音频接口 AXR4
  14. 安装Pytorch-gpu版本(第一次安装 或 已经安装Pytorch-cpu版本后)
  15. c语言switch excepted,C语言问题 expected unqualified-id
  16. Python实战:一键导出微信读书的书籍和笔记
  17. CSP 201809 第二题 买菜
  18. 如何自己制作小程序?
  19. SPI通讯介绍 以及读写W25Q64(块,扇区,页的区别)
  20. 常州2021高考成绩查询,常州2021高考成绩排名榜单,常州各高中高考成绩喜报

热门文章

  1. JavaScript之form(表单)
  2. 苹果logo_苹果,太会玩LOGO了~
  3. 记:《洛克菲勒留给儿子的38封信》-- 22
  4. 公司注册网站有哪些流程呢?
  5. cocos2d-JS engine--cc模块 概述
  6. WIN7远程桌面连接方法
  7. keepass2android 1.0.3,Keepass2Android离线版
  8. 100 数据结构教程
  9. 外贸员的日常工作分享
  10. 追梦之旅,从这里开始