12.3 性能分析方法

“曾经有那么一段时间,高性能处理器的结构完全由一个智者通过拍脑袋来决定,这段时间已经一去不复返了。现代处理器的结构是由一个队伍持续地改进和创新的结果,开发未来Alpha处理器的队伍是受到性能建模的方法所指导的。”这一段话源自Alpha处理器设计者所写的关于处理器性能分析的文章。现代处理器的设计者们需要采用高度系统化的流程来保证其设计能充分利用技术的进步并尽可能地适应目标工作负载的特性。计算机系统的设计涉及几个步骤。首先,理解和分析机器中要执行的应用或工作负载的特性,然后提出创新性的可选设计方案,接着对所提出的方案进行性能评估,最后选择最好的设计。但实际问题不像步骤流程所描述的那么简单,因为存在大量的可选设计方案,这些方案又和海量的各种工作负载交织在一起,这就导致设计和选择阶段变得非常棘手。

表12.10列出了性能分析和评估技术的分类,主要可以分为两类:性能建模和性能测量。性能建模又可以细分为基于分析和统计的建模和基于模拟的建模。性能建模主要用于设计过程的早期阶段,那个阶段还没有实际系统或者实际系统还不能用于性能评估,这时一些设计的决策都是基于性能模型。分析建模根据对处理器结构以及程序特性的分析,用一定的方法建立处理器的性能公式,然后将体系结构参数及程序特性参数作为输入,用数学公式计算出处理器的性能信息,通常使用数学的方法来创建概率模型、队列模型、马尔可夫模型或者Petri网模型。模拟建模是处理器设计中用得最为广泛的方法,采用模拟器进行性能建模,用于预测现有的或者一个新的系统的性能。模拟器的优点是灵活和开销小,通常用高级语言编写,其缺点是需要大量的时间或大量的计算资源来精准地建模一个相对较大和复杂的系统。通常,模拟器比真实硬件的速度慢几个数量级,模拟的结果也会受到人为因素的干扰,因为模拟器会带来一些非真实的假设。相对于模拟器而言,基于实际机器或者原型系统的性能测量方法会更为精准一些。性能测量是最可信的方法,可以精准地评估系统的性能。实际系统出来之后,性能分析和测量也是非常有价值的:一是需要理解在各种真实工作负载下的实际系统的性能,二是要验证实际机器硬件是否和设计规范要求的性能相吻合,三是发现可能的性能瓶颈以及识别出未来设计中需要做出的修改。实际机器的测量还可以帮助验证设计阶段采用的性能模型,提供了一个附加的反馈用于未来的设计。性能测量的主要缺点是只能测量现有配置的性能,而系统的现有配置通常不能改变,或者只能有限地重新配置。性能测量可以进一步分为片上硬件监测、片外硬件监测、软件监测和微码插桩。

表12.10 性能分析和评估技术的分类

好的性能建模和性能测量的技术和工具需要具备以下特性:

1)性能模型和测量结果要比较精确因为这些性能结果会直接影响到设计的权衡和选择。

2)性能模型的速度要比较快。如果一个性能模型很慢,在这个模型上执行工作负载需要花费数小时或者数天,那么做性能分析工作则需要花费数星期或者数月。性能模型的速度越慢,设计空间的探索范围就越有限。

3)性能模型和测量工具的开销不能太大,搭建性能评价和测量的平台不应该耗费大量的时间以及金钱。

4)性能模型上所执行的测试程序应该是目标代码,不需要应用程序的源代码,否则就不能对一些商业的应用程序进行性能评价。

5)性能模型要能捕获用户态和内核态的行为特性,能测量机器中所有的活动部分。有些工具只能测量或者捕获用户程序的行为,这对于传统的科学计算程序和工程程序是可以接受的。然而数据库的应用、Web服务器和Java的工作负载涉及大量的操作系统代码的执行,这就需要性能模拟和测量工具也能捕获操作系统的活动。

6)性能模型和测量工具需要能很好地利用多处理器系统资源,并且能应对多线程的应用,因为当前的应用越来越多是多线程的应用。

7)性能模型和测量工具应该是非侵入式的,因为侵入式的方法会在测量过程中改变系统的行为和大幅度地降低系统的性能。

8)性能模型和测量工具应该很容易修改和扩展,能对新的设计进行建模和测量。

当然,这些需求通常是相互冲突的。例如,不可能建模一个又快又精准的模型,数学分析建模是最快的方法也是最不精准的方法。又如,图形化(GUI)的显示方式增加了用户的友好性,但图形化的模拟器也会变得很慢。

12.3.1 分析建模的方法

分析建模的方法试图快速并且有效地捕获计算机系统的行为,并且对许多性能问题快速提供答案。由于处理器是个巨复杂系统,很难用一个模型来准确描述,所以可以独立地为处理器的各个部分建立分析模型,在忽略硬件实现细节的情况下考虑其对性能的影响,从而能够量化地分析影响处理器性能的因素,且具有速度快、灵活性高的特点。举一个最简单的分析建模的例子。计算系统的平均内存访问时间T=Tc+(1-h)Tm,T表示平均内存访问时间,h是Cache命中率,Tc是Cache的访问时间,Tm是内存的访问时间。这个模型对计算机存储系统的行为进行了简化建模,但也体现了某些特定参数的改变所产生的影响,例如Cache命中率的变化、内存访问时间的变化,都会对平均内存访问时间产生影响。

分析建模的方法中常常用到主成分分析方法。主成分分析方法也称为矩阵数据分析,是数学上用来降维的一种方法,其原理是设法将原来的变量重新组合成一组新的互相无关的综合变量,同时根据实际需要从中可以取出少数几个综合变量尽可能多地反映原来变量的信息。主成分分析方法可以用于分析程序中微体系结构无关的特性,以及对基准测试程序进行冗余性分析。

分析建模具有很好的成本效益,因为它们都是基于数学的建模方法。然而,分析建模方法通常对模型做了一些简化假设,这样在分析模型中并没有捕获某实际系统所拥有的典型细节。目前,分析建模尚未在处理器设计中得到广泛应用,这一方面是由于其构建非常复杂,需要对处理器的结构特性有深刻的认识,另一方面在于抽象模型无法反映硬件细节,误差较大。尽管对于处理器的建模不是很流行,但是分析建模的方法很适合评价大的计算机系统。计算机系统通常可以被看作一组硬件和软件资源,以及一组任务或者工作同时在竞争这些资源,分析建模就是一个合适的性能度量方法。

分析建模的一个典型应用是生成模拟建模的踪迹程序。具体做法是先使用统计方法对工作负载进行分析,并利用这些统计信息(包括指令类型分布、指令组合、指令间相关信息、分支预测率和Cache命中率等)综合生成踪迹程序。由于这种合成的程序实际上是工作负载的抽象,所以它包含的信息精炼,从而能在模拟器上得到很快的收敛。同时,这种方法也具有较高的准确性。

12.3.2 模拟建模的方法和模拟器

模拟建模的方法使用软件的方式来模拟计算机系统硬件在体系结构层面的功能和性能特性。模拟器是体系结构研究和设计的重要工具,在学术界和工业界得到广泛使用。模拟器需要具有运行速度快、模拟精度高、配置修改灵活等特点,但这些需求是相互矛盾的,很难兼得。模拟器贯穿系统设计的整个流程,只是在不同的设计阶段模拟的详细程度有所不同。初期阶段,模拟器可以用来对各种设计方案进行粗粒度模拟,通过比较模拟结果来选择最优设计方案。中期阶段,模拟器用来对各种微结构设计进行评估,对一些微结构参数的选择进行折中。后期阶段,模拟器需要和目标系统进行性能验证,保证性能模型和实际机器的吻合。系统完成之后,模拟器可以用来产生踪迹信息,对系统进行瓶颈分析和性能优化。由于模拟器具有上述重要作用,学术界和工业界都开发了大量的模拟器。常见的研究用的模拟器有SimpleScalar、SimOS、GEM5等,各个公司也都开发了自己的模拟器,如IBM公司的Mambo和Turandot模拟器、AMD的SimNow等。

模拟器使用C、C++和Python等高级语言开发,利用这些串行结构化语言的函数或者类来模拟计算机系统部件的功能和行为。模拟器最直接的方法就是将目标机器的二进制代码中的每一条指令都转换为同样语义的宿主机器的指令,在宿主机器上执行。通过将目标机器的每一个寄存器和标识位对应一个变量,目标机器的所有逻辑操作都可以被间接地翻译为变量的运算和各种各样的简单或者复杂的算法操作,这样目标机器上程序的所有操作都可以在宿主机器上以软件模拟的形式复现出来。这种翻译的方式实现起来比较简单,被多数模拟器所采用。在进行微结构级的详细模拟时,模拟器需要在时钟周期级别上记录每条动态指令的运行结果、每一级流水线触发器的状态、内部各种队列的状态以及体系结构寄存器的状态、内存和Cache的行为、分支预测器的状态等,其数据量巨大,从而导致详细模拟运行的速度缓慢。

下面介绍几个典型的模拟器,包括SimpleScalar、SimOS和GEM5。

SimpleScalar是一套完整的工具集,包括编译器、汇编器、连接器和库等,由威斯康星(Wisconsin)大学开发,1995年发布1.0版本。发布之后SimpleScalar迅速成为研究处理器设计的重要工具。SimpleScalar模拟的范围非常广泛,从非流水的单处理器到具有动态调度指令和多级高速缓存的多处理器系统。它支持多种指令集,包括Alpha、PowerPC、X86和ARM,还支持多种运行模式,包括sim-safe、simi-fast、sim-profile、sim-bpred、sim-cache、sim-eio和sim-outorder等。sim-safe模拟最为简单,仅仅模拟指令集。sim-outorder最为复杂,包括动态调度、推测执行等处理器技术,并且模拟了多级Cache存储系统。SimpleScalar采用了功能模拟和时序模拟分离的方法。功能模拟模块提供指令集和IO模拟,性能模拟部分提供模拟器运行的性能模型。指令集模拟部分采用解释执行的方式,将目标系统指令集解释执行。指令集解释器采用易于理解的目标定义语言,清晰地描述了指令执行过程中对寄存器或内存的修改,并使用预处理来判断依赖关系,产生SimpleScalar需要的代码。SimpleScalar的IO模拟模块提供了丰富的IO功能,可以满足通常的系统调用需要。性能模拟部分的核心是一个时钟计数器,它循环接收功能模拟部分传递过来的指令,根据指令类型来计算指令的执行周期。通常对于非访存指令,执行的周期都设置为1,而对于访存指令,则根据所模拟的存储层次做相应的计算。SimpleScalar的4.0版本MASE相对于之前的版本做了一些增强:功能模拟部分维持不变,性能模拟部分不再简单地计算延迟,而是使用一个微核心来执行指令。微核心中执行的指令根据系统的动态特征进行分支预测。在微核心中执行的指令提交阶段和功能模拟部分的执行结果进行验证,对于不正确的指令,则从上一次正确的地方重新执行,这样性能模拟部分就可以更好地给出系统的动态性能特征。之前的版本只有分支预测失败时才可以回滚到分支预测指令,而MASE则可以回滚到任意一条指令,这样就可以有效支持猜测读、处理器精确中断等技术。MASE中还增加了可变延迟的支持,更好地体现了系统的动态特征,比如系统DRAM对访存指令进行重排序所带来的访存延迟影响。SimpleScalar重点对CPU进行模拟,因此是研究CPU微结构的理想工具。但SimpleScalar不能启动操作系统,所有的应用需要用工具包中的工具进行编译链接才能执行。因此SimpleScalar上可以运行的应用程序受限,并且无法体现操作系统对系统的影响。

SimOS是斯坦福大学1998年开发的模拟器,可以模拟CPU系统或对称多处理器系统,包括存储系统、外设等。SimOS模拟的主要结构是SGI公司的MIPS和DEC公司的Alpha处理器,SimOS对系统各个部件的模拟都非常详细,可以启动和运行商业的操作系统。已经移植的操作系统包括IRIX和Linux(Alpha)等。由于SimOS可以运行完整的操作系统,所以在上面可以运行复杂的应用,如数据库和Web服务器等。为了达到速度和精确性的统一,SimOS提供了三种运行模式,分别是Embra、Mipsy和MXS。Embra采用动态二进制翻译的方法将目标指令集直接翻译成宿主机的指令集。这种模式通常用于操作系统和应用程序的启动。Mipsy模式也叫粗特性模式,包括缓存模拟和固定延迟的外设模拟。MXS模拟了详细的超标量处理器结构,该模型类MIPS R10000,采用了多发射、寄存器重命名、动态调度和带有精确异常的推测执行等技术。

GEM5模拟器是集成M5模拟器和GEMS模拟器的优点而高度定制化的模拟框架。GEM5使用C++和Python共同开发,其中C++编写底层功能单元,Python定制上层模拟器系统,整个开发方式是面向对象的,用户定制也非常方便。GEM5有4种可以相互替换的CPU模拟单元:AtomicSimpleCPU、TimingSimpleCPU、InOrderCPU和O3 CPU。其中AtomicSimpleCPU最简单,仅能完成功能模拟部分,TimingSimpleCPU可以分析内存操作的时序,InOrderCPU能够模拟流水线操作,O3 CPU(Out Of Order CPU)则能够模拟流水线的乱序执行并进行超标量等的时序模拟。在指令集方面,GEM5支持目前主流的X86、Alpha、ARM、SPARC和MIPS等架构。GEM5的模拟启动方式分为两种:一种是系统调用仿真模式,可以直接装载和执行目标机器的二进制应用程序;另外一种是全系统模拟模式,可以直接启动未修改的Linux 2.6操作系统内核。GEM5的模块化设计使得它在多核模拟上也体现出一定的优势,它不仅可以模拟各个部件,也能模拟部件之间的互连,这使得GEM5模拟器受到越来越多的重视。

为了在模拟速度和精度之间进行较好的折中,多种模拟加速技术被开发出来,其中有代表性的两种技术是采样模拟技术和统计模拟技术。采样模拟技术截取程序的一小部分进行模拟,并将其作为整个程序模拟结果的一个近似,如果采样程序能代表整个程序,则能获得较高的精度。通常采样方法有随机采样、SMARTS和SimPoint,其中SMARTS是对整个程序进行周期采样,SimPoint首先找到程序执行的相位(这里的相位表示程序在执行过程中重复出现的行为),然后对能够代表每个相位的部分进行采样和模拟仿真,这种采样方法大大减少了模拟时间。统计模拟技术和采样模拟技术一样,着眼于缩小需要模拟的指令数。该技术使用统计方法对工作负载进行分析,并利用这些统计信息(包括指令类型分布、指令组合、指令间相关信息、分支预测率和Cache命中率等)综合生成合成的程序踪迹,这种合成的程序踪迹实际上是对工作负载的抽象,所以它包含了提炼的信息,而且能够在模拟器上很快得到收敛。该方法在模拟速度和精度上有很大的优势,相对详细的模拟器,这种技术开发时间短,但是模拟的详细程度或覆盖度比详细的模拟及采样模拟技术差。

12.3.3 性能测量的方法

性能测量用于理解已经搭建好的系统或者原型系统。性能测量主要有两个目的,一是对现有的系统或者原型系统进行调优和改进,二是对应用进行优化。基本的过程包括:理解在这个系统上运行的应用的行为,将系统的特性和工作负载的特性进行匹配;理解现有系统的性能瓶颈;改进现有系统的设计特性,用于探索应用的特性。

计算机系统的性能测量主要包括基于硬件性能计数器的片上性能监测器、片外的硬件监测器、软件监测以及微码监测。本节重点介绍片上的硬件性能计数器,以及基于硬件性能计数器的Perf性能分析工具。

1.硬件性能计数器

所有的高性能处理器和主流的嵌入式处理器,包括龙芯3A2000及其以后的处理器、Intel公司的Core系列、AMD公司的K10和Bulldozer系列、IBM公司的Power系列和ARM公司的Cortex A系列处理器,都在片上集成了硬件性能计数器。硬件性能计数器能捕获处理器内部或者外部的各种性能事件,能在执行复杂、真实的应用时捕获处理器的性能行为和应用的行为。这种能力克服了模拟器上的限制,因为模拟器通常不能执行复杂的应用。当前,复杂的运行时系统涉及大量同时执行的应用,通过硬件性能计数器的方法可以很好地评估系统和实时地监测系统的行为。所有的处理器厂商都会在处理器用户手册上公布其性能计数器的信息。

在Intel的Nehalem处理器中,每个处理器核(Core)有3个固定的性能计数器,还有4个通用的性能计数器;每个处理器都有一个核外(Uncore)的性能监测单元(PMU),包括8个通用的性能计数器和一个固定的性能计数器。在Intel处理器中,性能计数器被称为型号特定寄存器(Model Specific Register,简称MSR),包括性能事件选择寄存器(PerfEvtSel0、PerfEvtSel1等)和性能计数寄存器PMC(PerfCtr0、PerCtr1等)两种,PerfEvtSel寄存器能使用RDMSR和WRMSR两条指令进行读和写,这两条指令只能在内核态执行,PerfCtr计数寄存器能使用RDPMC指令进行读取。PerfEvtSel寄存器用于选择需要监视哪些事件。PMC寄存器主要用于对所选择的事件进行计数,当PMC寄存器发生溢出时,会触发APIC中断,操作系统可以提供相应的中断向量用于处理计数器溢出产生的中断,通常需要在中断描述符表或中断向量表中提供一项,用于指向相应的例外处理函数。表12.11和表12.12分别给出了Intel Nehalem处理器和龙芯3A5000处理器的性能计数器部分事件示例。

表12.11 Intel Nehalem处理器性能计数器事件和描述

表12.12 龙芯3A5000处理器性能计数器事件和描述

性能测量和分析对于寻找处理器设计中的性能瓶颈、查找引发性能问题的原因以及优化代码具有重要的意义。表12.13给出了主流的基于硬件性能计数器的性能分析工具。Intel公司的商业化性能分析软件Vtune能使用Intel的性能计数器来进行性能测量。Compaq公司的DCPI(连续的剖析平台)在Alpha处理器上也是一个强大的程序剖析工具。系统工具Perf-mon能使用UltraSPARC处理器的性能计数器收集统计信息。Perf是Linux内核自带的用于收集和分析性能数据的框架,有时称为Perf事件或者Perf工具,早期称为Performance Counters for Linux(PCL),能用于统计指令执行数目、Cache失效数目、分支误预测失效数目等性能事件,也可以用于跟踪动态控制流,进行函数级和指令级的热点识别。OProfile是Linux系统的性能分析工具,是一个开源的profiling工具,它使用性能计数器来统计信息。OProfile包括一个内核模块和一个用户空间守护进程,前者可以访问性能计数寄存器,后者在后台运行,负责从这些寄存器中收集数据。AMD公司开发的CodeAnalyst是基于OProfile和Perf的开源的图形化性能分析工具,CodeAnalyst支持AMD的基于指令的采样技术(IBS),能更为精确地定位导致Cache失效或者流水线中断的指令。PAPI由田纳西大学开发,提供了一套访问性能计数器的API,使用PAPI需要对源码进行插桩,并且需要安装perfctr内核模块。

表12.13 处理器性能分析工具

下面主要介绍Linux平台中最常用的性能分析工具Perf。

2.Perf性能分析工具

Perf是集成在Linux内核中的性能分析工具,也称为perf_events或者perf tools,从Linux内核2.6.31开始集成到内核中。Perf用于收集和分析性能数据,在用户态就可以对应用进行剖析。其实现包括两个部分,用户态部分为perf程序,内核部分包括对处理器硬件计数器的操作、计数器溢出中断处理程序以及新增的系统调用sys_perf_event_open。依赖硬件平台提供的硬件事件支持或者内核提供的软件事件支持,perf能够对CPU和操作系统性能相关的指标进行采样,用较低的开销收集运行时性能数据。硬件支持的事件包括提交的指令、执行的时钟周期数、Cache失效次数和分支误预测次数等,软件支持的事件包括进程上下文切换、缺页、任务迁移和系统调用的次数等。通过将采样的事件和采样时的指令地址对应,perf可以识别哪段代码出现某种事件的频率较高,从而定位热点代码。通过各种事件统计,Perf也能监测程序在操作系统层面对资源的利用情况,协助理解内核和操作系统的行为。

Perf的工作原理如图12.2所示。Perf通过调用sys_perf_event_open系统调用来陷入内核,采样硬件事件时,内核根据Perf提供的信息在CPU的性能监测单元(Performance Monitoring Unit,简称PMU)上初始化硬件性能计数器(Performance Monitoring Counter,简称PMC)。每当特定事件发生时,PMC会随着增加,当PMC发生溢出时,会产生性能计数器溢出中断(Performance Counter Interrupt,简称PCI)。内核在PCI的中断处理程序中记录PMC的值,以及产生中断时正在执行的指令所在地址PC、当前时间戳以及当前进程的进程号PID或线程号TID、程序的符号名称comm等数据,把这些数据作为一个采样。内核把记录的采样拷贝到环形缓冲区中,位于用户态的perf利用mmap内存映射从环形缓冲区复制采样数据,并对其进行解析。Perf按照PID、comm等数据定位相应的进程,按照指令地址PC和可执行与可链接格式(ELF)文件中的符号表定位产生PCI中断的指令所属函数。PMC会被中断处理程序按照初始化时设定的采样周期重新置位恢复初始化时的值,在后续的运行过程中再次开始计数、触发中断,直到整个Perf采样过程结束。

图12.2 Perf的工作原理图

Perf是一个包含22种子工具的工具集,每个工具分别作为一个子命令。annotate命令读取perf.data并显示注释过的代码;diff命令读取两个perf.data文件并显示两份剖析信息之间的差异;evlist命令列出一个perf.data文件的事件名称;inject命令过滤以加强事件流,在其中加入额外的信息;kmem命令为跟踪和测量内核中slab子系统属性的工具;kvm命令为跟踪和测量kvm客户机操作系统的工具;list命令列出所有符号事件类型;lock命令分析锁事件;probe命令定义新的动态跟踪点;record命令运行一个程序,并把剖析信息记录在perf.data中;report命令读取perf.data并显示剖析信息;sched命令为跟踪和测量内核调度器属性的工具;script命令读取perf.data并显示跟踪输出;stat命令运行一个程序并收集性能计数器统计信息;timechart命令为可视化某个负载在某时间段的系统总体性能的工具;top命令为系统剖析工具。下面以示例的方式给出了一些常用子命令的用法。

perf list。perf list用来查看Perf所支持的性能事件,有软件的,也有硬件的。这些事件因系统性能监控硬件和软件配置而异。命令格式为:


$ perf list [hw | sw | cache | tracepoint | event_glob]

其中,hw和cache是CPU架构相关的事件,依赖于具体硬件;sw实际上是内核的计数器,与硬件无关;tracepoint是基于内核的ftrace。

下面为Nehalem平台中显示的perf list输出。

其中rNNN可以表示Perf中没有出现的CPU的“裸”事件。具体的硬件事件需要参考处理器的开发者手册,如Intel开发者手册的第三卷Performance Events部分。

perf top。执行与top工具相似的功能。对于一个指定的性能事件(默认是CPU周期),显示事件发生次数最多的函数或指令。perf top主要用于实时分析各个函数在某个性能事件上的热度,能够快速定位热点函数,包括应用程序函数、模块函数与内核函数,甚至能够定位到热点指令。支持的参数包括:-p<pid>,仅分析目标进程及其创建的线程;-e<event>,指明要分析的性能事件;-G,得到函数的调用关系图。支持的交互式命令中,a表示annotate current symbol,注解当前符号,能够给出汇编语言的注解,给出各条指令的采样率。

perf stat。用于分析指定程序的性能概况。为常见性能事件提供整体数据,包括执行的指令数和所用的时钟周期。命令格式为:


perf stat [-e <EVENT> | --event=EVENT] [-a] - <command> [<options>]

常用的参数为:-p,仅分析pid目标进程及其创建的线程;-a,从所有CPU上收集系统范围的性能数据;-r,重复执行命令求平均;-C,从指定CPU上收集性能数据;-v,表示verbose,显示更多性能数据;-n,只显示任务的执行时间;-o file,指定输出文件;--append,指定追加模式。下面给出了利用perf stat来分析164.gzip程序的示例。其中perf stat后面接的是可执行程序gzip_base.gcc4-nehalem-550,后续接的是gzip程序的参数,input.source表示gzip程序的输入集,60表示gzip压缩和解压缩执行60次。


$ perf stat ./gzip_base.gcc4-nehalem-550 input.source 60

上述示例表明gzip程序执行了14.26秒,执行的指令数目为51583076123,时钟周期数目为45574155874,IPC的值为1.13。CPU的利用率为99.9%,说明这是一个CPU-bound(CPU密集型)程序。前端堵的比例为41.30%,前端堵通常表现为分支误预测或者指令Cache失效,从上述可以看出分支指令的比例为20.1%,这个比例比较高,而且分支误预测率高达5.49%,也高于其他的SPEC CPU程序,同时,该Nehalem处理器每秒处理分支指令为7.27亿条,也就是说它处理分支指令的吞吐率也是较高的。perf stat是处理器性能分析中用得最多的命令之一。

下面给出了采用-e参数统计各种Cache和TLB事件的示例。-e后面接的是各种事件名称,事件和事件之间采用逗号隔开。


 $ perf stat -e L1-dcache-loads,L1-dcache-load-misses,L1-dcache-stores,L1-dcache-store
-misses,L1-icache-loads,L1-icache-load-misses,LLC-loads,LLC-load-misses,LLC-stores,LLCstore-
misses ./gzip …

上述示例给出了164.gzip程序访存的行为和Cache失效率,如L1 dcache load的失效率为12.59%,LLC(也就是三级Cache)load的失效率为8.58%,这些数据反映出gzip程序访存操作的Cache失效率比较高,这样内存访问延迟对于164.gzip程序就比较重要。

perf record。将性能数据记录到当前目录下的perf.data,随后可使用perf report或者perf annotate命令来进行性能分析。下面命令为采用perf record记录164.gzip程序执行时的剖析信息。


$ perf record ./gzip_base.gcc4-nehalem-550 input.source 60

perf report。此命令从上述perf record命令产生的perf.data文件中读取采样的性能数据,分析记录数据并把剖析的信息以用户可读的方式显示出来。


$ perf report

上述示例是基于时钟周期的采样所得到的程序的剖析,事件发生了45812415332次,而采样的次数为57000次。从上述输出可以看出,164.gzip程序中longest_match函数耗费了55.13%的时间,deflate函数耗费了9.50%的时间,inflate_codes函数耗费了6.87%的时间。


$ perf record -e branch-misses ./gzip_base.gcc4-nehalem-550 input.source 60

上述示例是基于分支误预测次数的采样,可以看出longest_match函数由于分支指令误预测所占的比例为55.55%,而deflate函数所占的比例提高到11.67%。


$ perf record -e LLC-loads ./gzip_base.gcc4-nehalem-550 input.source 60

上述示例是基于LLC load次数的采样,可以看出longest_match函数LLC load访问所占比例为44.74%,而deflate函数所占的比例提高到34.55%。这时就需要适当考虑分析deflate函数访存的行为,因为其LLC访问所占的比例较高。

perf trace。执行与strace工具相似的功能,它监控特定线程或进程使用的系统调用以及该应用程序接收的所有信号。

perf annotate。读取perf.data文件,并显示注释过的代码。下面给出了使用perf annotate命令对164.gzip程序的perf.data数据进行注释的示例。


$ perf annotate

上述示例给出的是基于时钟周期进行perf record,然后使用命令perf annotate所获得的剖析以及源代码和汇编代码的注释。由上可以看出longest_match函数中while循环的条件语句是一个复杂的条件判断,包含了一个数组元素的访问,然后再进行比较,其分别耗费了10.58%和11.74%的时间,而do语句的起始位置40235e处的指令耗费了31.95%的时间,其中cur_match变量正是来自while条件中的cur_match=prev[cur_match&WMASK],所以其时间也是归于do while分支的开销。也就是说通过perf annotate命令可以看出,longest_match函数大部分时间都是耗在复杂的条件判断和分支指令上。

Perf是一款优秀的性能分析工具,能够实现对单个进程或整个系统进行性能数据采样的工作,能采集更为精准的信息,并且Perf有较低的系统开销。