分享到
程序性能优化
 

作者:yfkiss,发布于2012-1-4

 

1. 故事

背景:线上流式计算,某个关键模块Mario一个大业务版本(带来输入数据double)升级上线

注:流式计算的典型范式之一是不确定数据速率的事件流流入系统,系统处理能力必须与事件流量匹配。

故事分为3个阶段

1)上线后,线上报警,Mario出现数据积压(处理能力无法满足当前线上流量)。

经查:Mario中经过处理后的数据需要进入远程数据库,处理线程以同步的方式将数据插入远程数据库,这种方式,使得线程处理能力急剧下降。

解决:数据写入磁盘,另外一个程序入库

2)第一个问题解决后,再次出现性能问题

解决:使用Tcmalloc(参考:http://blog.csdn.net/yfkiss/article/details/6902269)

3)使用Tcmalloc之后,发现线上CPU抖动非常厉害,并且有一定概率程序hang住

经查:一个求去重后的数据个数的算法,采用字典进行计算,频繁的对字典进行构建和删除,使得系统频繁申请、释放内存,从而导致cpu抖动。

解决:对于小数据,采用O(n^2)的算法,对于大数据,采取O(n)的算法(http://blog.csdn.net/yfkiss/article/details/6754786)。

2. 原理

程序性能优化可以做三个层次的事情。

1)设计

2)算法&数据结构

3)代码

当然,以上三个层面只是一般程序员可以做的优化,之上还有架构,之下还有运行系统和硬件。

设计:个人理解是最重要的一块,包括:数据如何处理?多线程还是单线程?多线程之间如何同步?锁粒度多大?是否使用内存池?同步还是异步等等

算法和数据结构:对算法优化往往可以使得程序性能有数量级的飞跃。

代码调优:运行中的程序有一种典型情况:20%的代码占了80%的运行时间,优化的重点是这20%的代码。

回到story,第一个阶段的问题,很明显是设计出现问题,在出现需要网络交互的时候的,考虑异步方案。

第二个阶段使用了tcmalloc,本质上是从设计、算法、代码多个角度对内存分配做了优化,只是这个优化是别人帮你做的~

第三个阶段属于算法优化,原有算法非常快,但带来了内存操作的过大开销,我们的应用中,数据集99%都非常小(数据集平均大小为2),因此,对于小数据集,采用O(n^2)的算法,对于大数据集,采用O(n)的算法,实际证明非常有效。所以,没有最好的算法,只有最适合的算法。

3. 如何找出热点代码

1)梳理程序,找出执行热点。很土,但是很有效

2)辅助工具:Google Cpu Profiler

方法1更多的是依靠经验,辅助工具Google Cpu Profiler简要介绍下。

Google Cpu Profiler是 google-perftools的一部分(google-perftools还包括Tcmalloc、Heap checkedr、Heap profiler)

其使用非常简单:

链接 profiler库及设置环境变量CPUPROFILE

4.使用Google Cpu Profiler进行性能分析的一个实例(使用 LD_PRELOAD,懒人法,不需要重编译)

#include <iostream>

#include <time.h>

using namespace std;

const int MAX_OPERATION = 2;

enum TYPE{MINUS = 0, PLUS};

int random(unsigned int n)

{

    if(n != 0)

    {

        return rand() % n;

    }

    else

    {

        return 0;

    }

}

void make_expression(int n)

{

    int left = random(n);

    int operation = random(MAX_OPERATION);

    int right = (PLUS==operation ? random(left) : random(n));

    cout << left << (operation==PLUS ? "-" : "+") << right << "=";

}

void make(int n, int max)

{

    for(int i = 1; i <= n; i++)

    {

        make_expression(max);

        if(0 != i % 3)

        {

            cout << "\t" << "\t";

        }

        else

        {

            cout << endl;

        }

    }

}

int main(int argc, char** argv)

{

    srand((int)time(0));

    if(argc != 3)

    {

        cout << "we need 3 argc" << endl;

        return 1;

    }

    make(atoi(argv[1]), atoi(argv[2]));

    cout << endl;

    return 0;

}

设置环境变量 LD_PRELOAD和CPUPROFILE

export "LD_PRELOAD=/home/work/zhouxm/google-perf_1.8.3/lib/libprofiler.so"

export "CPUPROFILE=/home/work/zhouxm/google-perf_1.8.3/bin/myprofiler"

注:LD_PRELOAD指定在程序运行前优先加载的动态链接库。这个功能主要就是用来有选择性的载入不同动态链接库中的相同函数。通过这个环境变量,我们可以在主程序和其动态链接库的中间加载别的动态链接库,甚至覆盖正常的函数库。这个环境变量相当危险,慎用

CPUPROFILE指定profiler文件保存位置及文件名

运行:

$./test 10000000 10000 1>/dev/null

PROFILE: interrupts/evictions/bytes =508/228/12704

分析:

1)文本分析:

$ ./pprof -text ./test ./myprofiler

Using local file ./test.

Using local file ./myprofiler.

Removing killpg from all stack traces.

Total: 508 samples

149 29.3% 29.3% 149 29.3% __write_nocancel

47 9.3% 38.6% 47 9.3% fwrite

41 8.1% 46.7% 41 8.1% _IO_file_xsputn@@GLIBC_2.2.5

41 8.1% 54.7% 41 8.1% random

33 6.5% 61.2% 33 6.5% std::operator<<

32 6.3% 67.5% 32 6.3% std::basic_ostream::operator<<

29 5.7% 73.2% 29 5.7% std::has_facet

26 5.1% 78.3% 26 5.1% std::num_put::_M_insert_int

15 3.0% 81.3% 15 3.0% std::basic_ostream::sentry::sentry

14 2.8% 84.1% 97 19.1% make_expression

13 2.6% 86.6% 73 14.4% std::num_put::do_put

11 2.2% 88.8% 11 2.2% random_r

9 1.8% 90.6% 9 1.8% strlen

7 1.4% 91.9% 7 1.4% CXXABI_1.3

7 1.4% 93.3% 7 1.4% std::basic_ostream::put

6 1.2% 94.5% 135 26.6% make

4 0.8% 95.3% 4 0.8% _IO_do_write@@GLIBC_2.2.5

4 0.8% 96.1% 4 0.8% _init

4 0.8% 96.9% 4 0.8% std::time_put::put

3 0.6% 97.4% 3 0.6% _IO_file_write@@GLIBC_2.2.5

3 0.6% 98.0% 3 0.6% fflush

3 0.6% 98.6% 3 0.6% std::__numpunct_cache::_M_cache

2 0.4% 99.0% 2 0.4% __gnu_cxx::stdio_sync_filebuf::file

2 0.4% 99.4% 2 0.4% std::basic_ios::widen

2 0.4% 99.8% 2 0.4% std::endl

1 0.2% 100.0% 1 0.2% rand

0 0.0% 100.0% 1 0.2% _DYNAMIC

0 0.0% 100.0% 8 1.6% __bss_start

0 0.0% 100.0% 143 28.1% __libc_start_main

0 0.0% 100.0% 143 28.1% main

2)图形分析

$ ./pprof -dot ./test ./myprofiler > test.dot

可是使用Graphviz打开dot文件


相关文章

深度解析:清理烂代码
如何编写出拥抱变化的代码
重构-使代码更简洁优美
团队项目开发"编码规范"系列文章
相关文档

重构-改善既有代码的设计
软件重构v2
代码整洁之道
高质量编程规范
相关课程

基于HTML5客户端、Web端的应用开发
HTML 5+CSS 开发
嵌入式C高质量编程
C++高级编程


Visual C++编程命名规则
任何时候都适用的20个C++技巧
C语言进阶
串口驱动分析
轻轻松松从C一路走到C++
C++编程思想
更多...   


C++并发处理+单元测试
C++程序开发
C++高级编程
C/C++开发
C++设计模式
C/C++单元测试


北京 嵌入式C高质量编程
中国航空 嵌入式C高质量编程
华为 C++高级编程
北京 C++高级编程
丹佛斯 C++高级编程
北大方正 C语言单元测试
罗克韦尔 C++单元测试
更多...   
 
 
 
 

 
 

组织简介 | 联系我们 |   Copyright 2002 ®  UML软件工程组织 京ICP备10020922号

京公海网安备110108001071号