C++ Perf性能调优+火焰图调优

16 篇文章 0 订阅
本文介绍了Linux性能分析工具PERF,演示了如何使用它进行性能剖析和代码优化,以及BrendanGregg的火焰图技术在理解和优化C++程序性能中的应用。通过实例展示了如何识别热点函数并进行改进,以提高软件性能。
摘要由CSDN通过智能技术生成

目录

前言

一、PERF是什么

二、火焰图是什么

三、c++代码优化示例


前言


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

一、PERF是什么


Linux性能计数器是一个基于内核的子系统,它提供一个性能分析框架,比如硬件(CPU、PMU(Performance Monitoring Unit))功能和软件(软件计数器、tracepoint)功能。通过perf,应用程序可以利用PMU、tracepoint和内核中的计数器来进行性能统计。
PERF是用来进行软件性能分析的工具。通过它,应用程序可以利用 PMU,tracepoint 和内核中的特殊计数器来进行性能统计。它不但可以分析指定应用程序的性能问题 (per thread),也可以用来分析内核的性能问题,当然也可以同时分析应用代码和内核,从而全面理解应用程序中的性能瓶颈。

查看APP热点函数:
安装perf

sudo yum install perf

查看热点函数

ps aux|grep vtest
perf top -p PID

记录一定时间的数据:

perf record -p PID
perf report #需要按CTRL+C取消record,然后执行下面的

记录堆栈,显示调用图:

perf record -a --call-graph fp -p PID
perf report --call-graph --stdio

也可以打印到文件perf report --call-graph --stdio >t.txt。

二、火焰图是什么


火焰图(Flame Graph)是由Linux性能优化大师Brendan Gregg发明的,和所有其他的trace和profiling方法不同的是,Flame Graph以一个全局的视野来看待时间分布,它从底部往顶部,列出所有可能的调用栈。其他的呈现方法,一般只能列出单一的调用栈或者非层次化的时间分布。
火焰图类型
常见的火焰图类型有 On-CPU,Off-CPU,还有 Memory,Hot/Cold,Differential 。

火焰图类型横轴含义纵轴含义解决问题采样方式
On-CPUCPU占用时间调用栈找出CPU占用高的问题函数,分析代码热路径固定频率采样CPU调用栈


生成火焰图:
下载flame

git clone https://github.com/brendangregg/FlameGraph.git

1、首先使用 perf record 命令记录进程的 CPU 使用情况
命令:sudo perf record -e cpu-clock -g -p 16808 或者./t1
2、 使用 perf script 工具对 perf.data 进行解析
命令:sudo perf script -i perf.data &> perf.unfold
3、使用 Flame Graph 工具将 perf.unfold 中的符号折叠 //生成脚本文件
命令:sudo ./stackcollapse-perf.pl perf.unfold &> perf.folded
4、使用 Flame Graph 工具将 perf.folded 生成 svg 火焰图
命令:sudo ./flamegraph.pl perf.folded > perf.svg

火焰图整个图形看起来就像一个跳动的火焰,这就是它名字的由来:
每一列代表一个调用栈,每一个格子代表一个函数
纵轴展示了栈的深度,按照调用关系从下到上排列,最顶上格子代表采样时,正在占用 cpu 的函数。
横轴的意义是指:火焰图将采集的多个调用栈信息,通过按字母横向排序的方式将众多信息聚合在一起。需要注意的是它并不代表时间。
横轴格子的宽度代表其在采样中出现频率,所以一个格子的宽度越大,说明它是瓶颈原因的可能性就越大。
火焰图格子的颜色是随机的暖色调,方便区分各个调用信息。
其他的采样方式也可以使用火焰图, on-cpu 火焰图横轴是指 cpu 占用时间,off-cpu 火焰图横轴则代表阻塞时间。
采样可以是单线程、多线程、多进程甚至是多 host。

三、c++代码优化示例


编译:
g++ test.cpp -o mytest -std=c++11 -fno-elide-constructors

#include <iostream> 
#include <unistd.h> 
#include <string.h> 
#include <iostream>
#include<fstream>
int funC(char* pData,int size)
{
    const char *recordpath = "/opt/eDisk/davit.test"; 
    std::ofstream outfile;
    outfile.open(recordpath, std::ios_base::out|std::ios_base::app);
    outfile.write((const char*) pData, size);
    return 0;
}
int funB()
{
    int size = 1024*1024*2;
    char *pAddr = new char[size];
    memset(pAddr,0,size);
    funC(pAddr,size);
    if(pAddr){
        delete [] pAddr;
        pAddr = nullptr;
    }
    return 0;
}

int funA()
{
    sleep(2);
    funB();
    return 0;
}

int main(int argc, char* argv[]) 
{
    while(true){
        funA();
    }
    return 0;
}

ps aux|grep mytest
perf top -p PID

发现热点函数是:
1、__memset_sse2
2、_IO_file_fopen

通过上面堆栈、将指针的分配改为全局变量、只分配一次、释放一次,经过优化,代码如下:

#include <iostream> 
#include <unistd.h> 
#include <string.h> 
#include <iostream>
#include<fstream>
char *pAddr;
int size = 1024*1024*2;

int funC(char* pData,int size)
{
    const char *recordpath = "/opt/eDisk/davit.test"; 
    std::ofstream outfile;
    outfile.open(recordpath, std::ios_base::out|std::ios_base::app);
    outfile.write((const char*) pData, size);
    return 0;
}
int funB()
{
    funC(pAddr,size);
    return 0;
}

int funA()
{
    sleep(2);
    funB();
    return 0;
}

int main(int argc, char* argv[]) 
{
    
    pAddr = new char[size];
    memset(pAddr,0,size);    
    while(true){
        funA();
    }
    if(pAddr){
        delete [] pAddr;
        pAddr= nullptr;
    }
    return 0;
}


再次监控堆栈,分析热点函数,如下,明显已经得到了优化


经过优化后,火焰图如下

火焰图分析技巧:
纵轴代表调用栈的深度(栈桢数),用于表示函数间调用关系:下面的函数是上面函数的父函数。
横轴代表调用频次,一个格子的宽度越大,越说明其可能是瓶颈原因。
不同类型火焰图适合优化的场景不同,比如 on-cpu 火焰图适合分析 cpu 占用高的问题函数,off-cpu 火焰图适合解决阻塞和锁抢占问题。
无意义的事情:
横向先后顺序是为了聚合,跟函数间依赖或调用关系无关;
火焰图各种颜色是为方便区分,本身不具有特殊含义

通过上面火焰图分析:发现_libc_writev、__GI___libc_nanosleep、__fopen_internal、__GI___libc_close等可能是瓶颈、要结合代码来一并分析

总结
通过本文介绍应该对PERF有了基本的认识,还有很多方法来查看软件的热点函数,如GPERF、GPROF等,你可以自己深入去研究,本文希望能够起到抛砖引玉的作用。

                        
原文链接:https://blog.csdn.net/weixin_44834554/article/details/127188420

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值