记一次使用gperftools优化线上程序

gperftools的实战使用经验

项目自上线之后一直有玩家反应战斗的时候会有不太流畅的情况出现,尤其是人数多,生成战斗对象多的情况下,经过对程序的监控并没有发现什么异常,主机资源的使用上也没发现什么问题,cpu和内存的使用率都不高,网络进出流量也并没有达到峰值,cpu使用率和网络的抖动从监控上看也看不出什么问题,这个问题困扰了我很久,一直苦于无法解决,不过通过我对cpu使用率的观察,感觉cpu虽然没有长时间的抖动,但是偶尔会有一个比较大的抖动出现,时间很短,但是抖动的范围还是比较大的,最后我找到了google的性能分析工具gperftools,因为我们的程序可以分布负载,可以用多个进程将一部分大的功能负载,这些进程本身是没有什么区别的,可以随意动态增加减少,所以我拿出了一个线上程序做了这次实验,把gperftools的相关库链接到了我的程序上并添加相关代码编译后放到线上环境中去运行,运行24小时候后停止程序并分析输出报告,然后解决报告中可见的问题,解决之后再次将新的程序放到线上运行24小时,如此循环一直到输出报告中看不到明显异常为止。 在这次实验中,确实发现了几个消耗cpu特别严重的函数调用,这些函数使用频繁,而且又非常耗费cpu,如果实例对象特别多的时候确实有可能造成cpu的抖动从而影响玩家正常体验。 把这些问题都完善之后确实收到了效果,反应战斗不流畅的玩家数量大幅减少,可以说这次优化还是比较成功的,记录一下gperftools的使用方法,gperftools可以分析cpu和内存的性能,下面分别写了例子和用法。

gperftools是google出品的一个性能分析工具,相关介绍可见:
https://github.com/gperftools/gperftools/wiki
gperftools性能分析通过抽样方法完成,默认是1秒100个样本,即一个样本是10毫秒,因此程序运行时间要长一些。

编译安装gperftools

https://github.com/gperftools/gperftools/releases 下载最新版本的gperftools源码包
解压到/usr/local/src目录,编译安装

cpu性能分析

#include <google/profiler.h>
#include <iostream>
using namespace std;
void func1() 
{
    for (int i = 0; i < 100000; ++i) 
    {
        ++i;
        char *p = (char*)malloc(1024);
        free(p);
    }  
}
void func2() 
{
    for (int i = 0; i < 200000; ++i) 
    {
        char *p = (char*)malloc(1024);
        free(p);
    }  
}
void func3() 
{
    for (int i = 0; i < 100000; ++i) 
    {
        func1();
        func2();
    }  
}

int main()
{
    ProfilerStart("test.prof");//开启性能分析并指定所生成的profile文件名
    func3();
    ProfilerStop();//停止性能分析并输出结果(如果这行代码不运行,将无法输出任何数据,只有一个空文件)
    return 0; 
}

编译上面的程序文件test.cpp,连接tcmalloc和profiler
g++ -I/work/trunk/ThirdParty/include/  -L/work/trunk/haizhan/Linux64_6.3.0_Debug/commonlib/ -Wl,-rpath,/work/trunk/haizhan/Linux64_6.3.0_Debug/commonlib/   -ltcmalloc -lprofiler   test.cpp -o test
运行程序,等待程序退出后输出分析
./text
将程序输出的分析结果解析成pdf,需要安装graphviz
yum install graphviz
/work/trunk/haizhan/Linux64_6.3.0_Debug/share_bin/pprof -pdf  test test.prof  > CpuProfiler.pdf

可以看到cpu在各个函数中的消耗,上面的百分比是本函数的消耗,下面的百分比是本函数所在堆栈所有函数的消耗,可以很清楚的分析出严重消耗cpu的问题函数。

CpuProfiler.pdf

我们线上项目的分析报告已经被我删掉了。。。很遗憾没法记录当时的程序运行情况和每天优化后重新运行的分析情况。


内存性能分析

#include <gperftools/heap-profiler.h>
#include <iostream>
using namespace std;
void func1() 
{
    for (int i = 0; i < 100000; ++i) 
    {
        ++i;
        char *p = (char*)malloc(1024);
        free(p);
    }  
}
void func2() 
{
    for (int i = 0; i < 200000; ++i) 
    {
        char *p = (char*)malloc(1024);
        free(p);
    }  
}
void func3() 
{
    for (int i = 0; i < 100000; ++i) 
    {
        func1();
        func2();
    }  
}

int main()
{
    HeapProfilerStart("test.prof");//开启内存分析并指定所生成的profile文件名
    func3();
    HeapProfilerStop();//停止内存分析并输出结果(如果这行代码不运行,将无法输出任何数据,只有一个空文件)
    return 0; 
}


编译,运行并输出pdf(同CPU性能分析),不同之处在于内存分析每产生1G的内存使用就会生成一个文件,生成pdf的时候需要包含所有生成的文件
/work/trunk/haizhan/Linux64_6.3.0_Debug/share_bin/pprof -pdf  test test.prof.*.heap  > out.pdf

MemoryProfiler.pdf

官方给出的建议是虽然gperftools对程序性能的影响不是非常大,也尽量不要在线上环境使用gperftools,但是经过我的使用发现gperftools大概会降低程序30%左右的效率,如果你的程序本身占用cpu并不高,而有些问题又必须在线上真实环境下才可能表现的出来,完全可以放到线上尝试一下。