《C程序性能优化》学习笔记【三】—— 寻找性能瓶颈

3.1 使用gprof命令进行分析

在追求高效化编程的过程中,若不能准确定位哪些环节在浪费时间,可先分析程序各部分在执行时花费的时间,然后找到原因,解决问题。

gprof的使用方法

gprof工具可以打印程序运行过程中各个函数消耗的时间与调用次数,也可以获得程序运行过程中函数调用关系图(call graph)的评测信息(profile information)。
使用gprof之前,有必要先对目标程序添加配置文件选项进行编译/链接。

$ gcc -p -o example example.c

配置文件选项可缩写为-pg(gprof)或者-p(prof)。
然后执行目标程序,统计程序内各函数执行时间以及调用次数,输出到gmon.out文件。

$ ./example

启动gprof后,gprof会将gmon.out中的数据域程序中的连接信息进行对照,输出统计好的评测信息。

$ gprof example

3.2 哪个环节在消耗时间

以程序sm2为例,截图3-1为其评测信息中被称为平面评测信息(flat profile)的部分。
在这里插入图片描述
其中:

  • self seconds 为函数本身所执行的时间,不包含子函数执行时间;
  • cumulative seconds 为函数上一行与“self seconds”的累计值。最后一行微程序整体执行时间,比通过time指令测试的时间短很多
  • self ns/call 为函数调用一次花费的时间,不包含子函数执行时间;
  • total ns/call 为函数调用一次花费的时间,包含子函数执行时间。

使用gprof的注意事项

  • “self”栏表示的时函数执行一次所花费的时间,有统计的函数总执行时间除以调用次数得到的平均值。子函数的执行时间会根据输入数据的变化发生较大波动,分析评测信息是需要考虑
  • gprof智能分析应用程序内代码的执行状况,无法查看输入输出等内核模式执行的模块。在输入输出占比较大的程序中,铜价时间与实际执行时间的误差较大
  • 程序执行中发生中断或非正常退出时,无法输出评测信息。

获取库函数的评测信息

图3-1中,只有用户定义函数的评测信息,却没有库函数的评测信息。
因为gprof工具根据程序链接时的函数链接信息生成评测信息,而程序使用了标准库的共享库,在执行时动态链接库函数,因此gprof结果不包括标准库的评测信息。

可以通过静态链接标准库解决上述问题:

$ gcc -p -static -o example example.c

gprof结果如图3-2所示。
在这里插入图片描述

耗时的函数

输出的评测信息时按照执行时间由长到短的顺序排列的。实现高效变成,可通过以下几个方面实施:

  • 优化自定义的程序;
  • 将库函数重写为更高效的函数;
  • 减少库函数的使用频率。

在图3-2中,vfprintf耗时26%,调整vfprintf对sprintf函数的调用是高效编程的关键。
sprintf按字符串的标准格式来编辑数据,所以在分析字符串时比较费时,低效。因此将sprintf重写为ull2str函数,测试结果图3-3。
在这里插入图片描述
从结果看,程序执行时间从之前的2.9s提升到1.69s,重写库函数将性能提高了40%。
参照重下下一个低效函数 __strtol_1_internal。

显示库函数的调用次数

使用默认链接的运行时库(libc.so),gprof不会打印库函数的调用次数和单次执行时间。
操作系统中有可以使用配置文件选项的运行时库(libc_p.a),重新链接,即可打印调用次数。例如:

$ gcc -p -static -o example example.c -lc_p

若操作系统中没有可使用的配置文件选项运行时库,可通过指定配置文件选项来编译库得到相应的运行时库。

3.3. 函数的调用关系

图3-4为图3-3的后续函数调用关系图。从构成sm2的main到IO_init共20个函数,表中会总了调用函数和子函数以及调用次数和执行时间等信息。
在这里插入图片描述
在这里插入图片描述
main[1]函数仅被运行时库的__libc_start_main调用一次,但却调用print函数等6个函数。ipow10[11]函数被main函数调用40万次,被get_data函数调用120万次,总计160万次,如图3-1所示。
在这里插入图片描述

3.4 进行数据分析的原理

统计调用次数

  • 程序在添加了配置文件选项的情况下进行编译时,编译器会在程序内个函数入口插入用于性能测试的代码片段。
  • 在指定了配置文件选项的情况下,链接器把能使用配置文件选项的运行时库链接起来,对开始和结束时插入的代码片段进行分析。
  • 程序执行时,编译是插入的代码片段会记录下函数的调用情况,记录下的只会被作为调用次数打印出来。、

统计执行时间

  • 为统计执行时间,每10ms中断一次正在执行的程序,记录程序计数器指示的执行地址。
  • 通过对比该地址和链接器的链接信息计算那些函数在被执行,从而统计每个函数执行时间。

gprof只分析主程序和连续链接的程序,为分析共享库内函数,输出结果的代码清单没有统计相关数据。

以下为图3-3的sm2程序通过time执行进行测试的结果。
在这里插入图片描述
从结果看,time指令所示用户CPU时间2.544s,比gprof测试的执行时间1.68s,多了近1s。原因如下:

  • time指令统计程序启动时的初始设置、程序自身的执行以及执行后的系统处理等一连串操作的执行时间总和;
  • gprof统计出事设置、分析处理及函数本身的时间总和。

因此,gprof理应比time时间短。而要在应用程序内准确分析程序执行的时间,使用gprof相对比较准确

3.5 其他性能分析器

虽然gprof存在缺陷,但在GCC的大部分操作环境中斗都安装了gprof,主要因为gprof操作简单
改善程序性能的性能分析器还有以下几款工具:

Oprofile
适用于Linux环境。组成如下:

  • 作为内核驱动程序被去安装的性能分析器;
  • 用于采集储存数据的后台程序;
  • 用来整理所采集数据的工具。

Oprofile利用CPU的性能计数器来中断程序,采用程序计数器的值,总结程序的执行情况。
虽然没有在程序中插入性能分析器的统计函数调用次数的代码,不能准确记录执行次数,但可分析稍低于内核与共享库的其他模块的执行情况,统计被忽略的评测程序本身花费的时间。

Intel的Vtune(收费)和AMD的CodeAnalyst(免费)

这两种不必插入分析器机能即可观察程序的运作,虽然使用该分析器的CPU有限制条件,但的确可检测CPU内部的详细运作情况。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值