摘要:本文主要讲解如何使用perf观察程序在缓存利用方面的瓶颈,进而优化程序,提高cache命中率。主要讲解提高缓存利用的几种常用方法。

1.程序局部性

一个编写良好的计算机程序通常具有程序的局部性,它更倾向于引用最近引用过的数据项,或者这个数据周围的数据——前者是时间局部性,后者是空间局部性。现代操作系统的设计,从硬件到操作系统再到应用程序都利用了程序的局部性原理:硬件层,通过cache来缓存刚刚使用过的指令或者数据,来提交对内存的访问效率。在操作系统级别,操作系统利用主存来缓存刚刚访问过的磁盘块;在应用层,web浏览器将最近引用过的文档放在磁盘上,大量的web服务器将最近访问的文档放在前端磁盘上,这些缓存能够满足很多请求而不需要服务器的干预。本文主要将的是硬件层次的程序局部性。

2.处理器存储体系

计算机体系的储存层次从内到外依次是寄存器、cache(从一级、二级到三级)、主存、磁盘、远程文件系统;从内到外,访问速度依次降低,存储容量依次增大。这个层次关系,可以用下面这张图来表示:

程序在执行过程中,数据最先在磁盘上,然后被取到内存之中,最后如果经过cache(也可以不经过cache)被CPU使用。如果数据不再cache之中,需要CPU到主存中存取数据,那么这就是cache miss,这将带来相当大的时间开销。

3.perf原理与使用简介

Perf是Linux kernel自带的系统性能优化工具。Perf的优势在于与Linux Kernel的紧密结合,它可以最先应用到加入Kernel的new feature。perf可以用于查看热点函数,查看cashe miss的比率,从而帮助开发者来优化程序性能。

性能调优工具如 perf,Oprofile 等的基本原理都是对被监测对象进行采样,最简单的情形是根据 tick 中断进行采样,即在 tick 中断内触发采样点,在采样点里判断程序当时的上下文。假如一个程序 90% 的时间都花费在函数 foo() 上,那么 90% 的采样点都应该落在函数 foo() 的上下文中。运气不可捉摸,但我想只要采样频率足够高,采样时间足够长,那么以上推论就比较可靠。因此,通过 tick 触发采样,我们便可以了解程序中哪些地方最耗时间,从而重点分析。

本文,我们主要关心的是cache miss事件,那么我们只需要统计程序cache miss的次数即可。使用perf 来检测程序执行期间由此造成的cache miss的命令是perf stat -e cache-misses ./exefilename,另外,检测cache miss事件需要取消内核指针的禁用(/proc/sys/kernel/kptr_restrict设置为0)。

4.cache 优化实例

4.1数据合并

有两个数据A和B,访问的时候经常是一起访问的,总是会先访问A再访问B。这样A[i]和B[i]就距离很远,如果A、B是两个长度很大的数组,那么可能A[i]和B[i]无法同时存在cache之中。为了增加程序访问的局部性,需要将A[i]和B[i]尽量存放在一起。为此,我们可以定义一个结构体,包含A和B的元素各一个。这样的两个程序对比如下:

test.c

#define NUM 393216  int main(){  float a[NUM],b[NUM];  int i;  for(i=0;i<1000;i++)  add(a,b,NUM);
}  int add(int *a,int *b,int num){  int i=0;  for(i=0;i<num;i++){  *a=*a+*b;  a++;  b++;  }
}

test2.c

#define NUM 39216
typedef struct{  float a;  float b;
}Array;  int main(){  Array myarray[NUM];  int j=0;  for(j=0;j<1000;j++)  add(myarray,NUM);
}  int add(Array *myarray,int num){  int i=0;  for(i=0;i<num;i++){  myarray->a=myarray->a+myarray->b;  myarray++;  }
}

注:我们设置数组大小NUM为39216,因为cache大小是3072KB,这样设定可以让A数组填满cache,方便对比。 对比二者的cache miss数量:

test

[huangyk@huangyk test]$ perf stat -e cache-misses ./test  Performance counter stats for './test':   530,787 cache-misses                                                    2.372003220 seconds time elapsed

test2

[huangyk@huangyk test]$ perf stat -e cache-misses ./test2    Performance counter stats for './test2':  11,636 cache-misses                                               0.233570690 seconds time elapsed

可以看到,后者的cache miss数量相对前者有很大的下降,耗费的时间大概是前者的十分之一左右。

进一步,可以查看触发cach-miss的函数:

test程序的结果:

[huangyk@huangyk test]$ perf record -e cache-misses ./test
[huangyk@huangyk test]$ perf report
Samples: 7K of event 'cache-misses', Event count (approx.): 3393820  45.88%  test  test               [.] sub  44.74%  test  test               [.] add  0.71%  test  [kernel.kallsyms]  [k] clear_page_c  0.70%  test  [kernel.kallsyms]  [k] _spin_lock  0.43%  test  [kernel.kallsyms]  [k] run_timer_softirq  0.41%  test  [kernel.kallsyms]  [k] account_user_time  0.34%  test  [kernel.kallsyms]  [k] hrtimer_interrupt  0.30%  test  [kernel.kallsyms]  [k] run_posix_cpu_timers  0.29%  test  [kernel.kallsyms]  [k] _cond_resched  0.24%  test  [kernel.kallsyms]  [k] update_curr  0.23%  test  [kernel.kallsyms]  [k] x86_pmu_disable  0.22%  test  [kernel.kallsyms]  [k] __rcu_pending  0.20%  test  [kernel.kallsyms]  [k] task_tick_fair  0.19%  test  [kernel.kallsyms]  [k] account_process_tick  0.17%  test  [kernel.kallsyms]  [k] scheduler_tick  0.16%  test  [kernel.kallsyms]  [k] __perf_event_task_sched_out

从perf输出的结果可以看出,程序cache miss主要是由sub和add触发的。

test2程序的结果:

perf record -e cache-misses ./test2
perf report
Samples: 51  of event 'cache-misses', Event count (approx.): 17438  39.78%  test2  [kernel.kallsyms]  [k] clear_page_c  15.68%  test2  [kernel.kallsyms]  [k] mem_cgroup_uncharge_start  7.94%  test2  [kernel.kallsyms]  [k] __alloc_pages_nodemask  7.28%  test2  [kernel.kallsyms]  [k] init_fpu  6.50%  test2  test2              [.] add  3.19%  test2  [kernel.kallsyms]  [k] arp_process  2.76%  test2  [kernel.kallsyms]  [k] account_user_time  2.73%  test2  [kernel.kallsyms]  [k] perf_event_mmap  2.02%  test2  [kernel.kallsyms]  [k] filemap_fault  1.62%  test2  [kernel.kallsyms]  [k] kfree  1.50%  test2  [kernel.kallsyms]  [k] 0xffffffffa04334b8  1.06%  test2  [kernel.kallsyms]  [k] _spin_lock  1.06%  test2  [kernel.kallsyms]  [k] raise_softirq  1.00%  test2  [kernel.kallsyms]  [k] acct_update_integrals  0.96%  test2  [kernel.kallsyms]  [k] __do_softirq  0.67%  test2  [kernel.kallsyms]  [k] handle_edge_irq  0.56%  test2  [kernel.kallsyms]  [k] __rcu_pending  0.40%  test2  [kernel.kallsyms]  [k] enqueue_hrtimer  0.39%  test2  test2              [.] sub  0.38%  test2  [kernel.kallsyms]  [k] _spin_lock_irq  0.36%  test2  [kernel.kallsyms]  [k] tick_sched_timer

从perf输出的结果可以看出,add和sub触发的perf miss已经占了很小的一部分。

4.2循环交换

C语言中,对于二维数组,同一行的数据是相邻的,同一列的数据是不相邻的。如果在循环中,让依次访问的数据尽量处在内存中相邻的位置,那么程序的局部性将会得到很大的提高。 观察下面矩阵相乘的几个函数:

test_ijk:

void Muti( double A[][NUM],double B[][NUM],double C[][NUM],int n){   int i,j,k;  double sum=0;  for (i=0;i<n;i++)  for(j=0;j<n;j++){  sum=0.0;  for(k=0;k<n;k++)  sum+=A[i][k]*B[k][j];  C[i][j]+=sum;  }

test_jki:

void Muti( double A[][NUM],double B[][NUM],double C[][NUM],int n){   int i,j,k;  double sum=0;  for (j=0;j<n;j++)  for(k=0;k<n;k++){  sum=B[k][j];  for(i=0;i<n;i++)  C[i][j]+=A[i][k]*sum;  }
}

test_kij:

void Muti( double A[][NUM],double B[][NUM],double C[][NUM],int n){   int i,j,k;  double sum=0;  for (k=0;k<n;k++)  for(i=0;i<n;i++){  sum=A[i][k];  for(j=0;j<n;j++)  C[i][j]+=B[k][j]*sum;  }
}

考察内层循环,可以发现,不同的循环模式,导致的cache失效比例依次是kij、ijk、jki递增。

4.3循环合并

在很多情况下,我们可能使用两个独立的循环来访问数组a和c。由于数组很大,在第二个循环访问数组中元素的时候,第一个循环取进cache中的数据已经被替换出去,从而导致cache失效。如此情况下,可以将两个循环合并在一起。合并以后,每个数组元组在同一个循环体中被访问了两次,从而提高了程序的局部性。

5.后记

实际情况下,一个程序的cache失效比例往往并不像我们从理论上预测的那么简单。影响cache失效比例的因素主要有:数组大小,cache映射策略,二级cache大小,Victim Cache等,同时由于cache的不同写回策略,我们也很难从理论上预估一个程序由于cache miss而导致的时间耗费。真正在进行程序设计的时候,我们在进行理论上的分析之后,只有使用perf等性能调优工具,才能更真实地观察到程序对cache的利用情况。

原文地址:https://www.ibm.com/developerworks/community/blogs/5144904d-5d75-45ed-9d2b-cf1754ee936a/entry/perf_introduction?lang=en

perf 性能分析实例——使用perf优化cache利用率相关推荐

  1. linux 汇编 perf,性能分析利器之perf浅析

    作为服务器后台开发,不仅仅要写业务逻辑,后台意味着高并发,稳定性,当你写了很多逻辑,发现性能有问题的时候,也要学会性能分析,进行性能优化, 也许你会接触很多性能分析工具:valgrind,gperft ...

  2. 嵌入式设备交叉编译perf性能分析工具

    嵌入式设备交叉编译perf性能分析工具 1.1 背景 最近工作一直在做嵌入式相关的开发,主要是应用方面,随着程序的业务功能越写越复杂,加上嵌入式系统上的调试工具,少之又少,主要还是靠printf的传统 ...

  3. 【ceph】ceph性能分析工具之perf dump代码打点调试统计PerfCounters

    目录 简介 一.查ceph自带的统计 1 命令格式 2 dump 命令输出结果 二.自己添加统计 三.分析案例 io耗时初分析 继续分析 进一步分析 代码修改和验证 四.报错记录 五.附录 1.代码修 ...

  4. 【ceph】ceph性能分析工具之perf dump代码打点调试统计

    目录 简介 一.查ceph自带的统计 1 命令格式 2 dump 命令输出结果 二.自己添加统计 三.分析案例 io耗时初分析 继续分析 进一步分析 代码修改和验证 四.报错记录 五.附录 1.代码修 ...

  5. linux性能分析工具:perf入门一页纸

    软件开发中程序运行一段时间出现2类问题最头疼: 1.突然崩溃(调试问题) Windows:可以用WinDbg分析core dump文件,使用应用程序验证器(appverif.exe)对程序进行全面检查 ...

  6. perf性能分析工具

    perf是performance的简称,最常用的性能分析工具.一款随linux内核代码一同发布和维护的性能诊断工具.linux内核2.6.31加入performance Counter, 内核2.6. ...

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

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

  8. 学会使用perf性能分析工具--这一篇就够了

    在功能上,perf很强大,可以对众多的软硬件事件采样,还能采集出跟踪点(trace points)的信息(比如系统调用.TCP/IP事件和文件系统操作.perf的代码和Linux内核代码放在一起,是内 ...

  9. Kernel:性能分析实例(一)

    https://download.csdn.net/download/qq_36428903/86726594

最新文章

  1. 漫谈算法(番外篇) 符号标记以及基本数学公式
  2. word在线解密_实用技能 | 5款免费在线转换PDF的网站
  3. java类的完整生命周期详解
  4. 【渝粤教育】国家开放大学2018年秋季 0014-21T秘书学(一) 参考试题
  5. Hex hsl 转换 php,关于 RGB,HEX,HSL 颜色相互转换
  6. Android Framework中的Application Framework层介绍
  7. tomcat6配置log4j日志
  8. Grunt上手指南(转)
  9. freecplus框架-加载参数文件
  10. Futter基础第5篇: 实现列表、动态列表【ListView、ListView.builder】
  11. centos7.1下的mariadb数据库数据出现不支持中文问题
  12. Ubuntu server配置远程VNC服务
  13. file is not a zip file_如何使用JavaScript解压压缩后的zip文件
  14. MATLAB之方程组求解(八)
  15. 十分钟django后台 simpleui -含自定义后台首页
  16. 关于PC端QQ无法加载群文件和打开在线群文件解决方法
  17. Ilog、Drools、Jess规则引擎的Rule Language 比对
  18. 自我思辨的力量-追求完美的内心驱动
  19. 《大数据项目实战之搜索引擎用户行为分析》
  20. 主机如何连接到URSim中的客户端接口

热门文章

  1. 【天光学术】新闻学论文:校园网络流行语传播社会热点问题的途径(节选)
  2. CPP----C++练习100题
  3. 华为机试 素数伴侣 匹配匈牙利算法
  4. 如何将原来Eclipse的一个工作空间设置好的界面复制到另外一个Eclipse里面
  5. 【Python 实战基础】Pandas如何统筛选复制某个数据
  6. jQuery仿天猫完美加入购物车
  7. 理科生学计算机数字媒体方向,文科生,理科生,分别可以报哪些专业?5分钟了解文理报考大方向...
  8. 15.(cesium之家)cesium暗色系地图样式地图(滤镜实现,反色滤镜)
  9. Carboxyrhodamine 110-PEG4-DBCO,羧罗丹明110-PEG4-DBCO是一种荧光标记染料
  10. Elastic:集群相关知识点总结(一)数据流 Data Stream、索引生命周期 ILM、可搜索快照 searchable snapshots、跨集群搜索 CCS、跨集群复制 CCR