C++ 性能剖析 (一)

性能问题也不是仅仅用“技术”可以解决的,它往往是架构,测试,假设等综合难题。不过,对于一个工程师来说,必须从小做起,把一些“明显”的小问题解决。否则的话积小成多,千里堤坝,溃于蚁穴。

C++ 的性能为什么总是排在C之后 (见http://benchmarksgame.alioth.debian.org/u32/performance.php?test=binarytrees 等网站的最新测试结果)?我认为这是3个方面的原因:

1)用于测试的C++ 编译器没有使用最新的优化技术

2)C++ 附加的价值没有考虑到测试之中

3)C++ 应用层面的“微妙性”(可参考我的关于C++的其他博客)使得一般程序员往往望而却步,选择“教科书用例”,使得一些副作用没有在应用层面被剔出。

记得10多年前,我在微软做开发时,曾向C++最早编译器的作者李伯曼(Stan Lippman)(时任微软VC++架构师)咨询过一系列我们小组的C++性能难题,在他的帮助下,我们在关键地方用了诸如inline,RVO等技术,完全解决了性能问题,还找出了VC++ 的几个不小的错误。我认识到,C++的性能问题多数在于我们对C++认识的浅薄,多数都是不难解决的。

下面用一例子,来做一下对比,看看一些微妙的细节是如何影响程序性能的。

struct intPair

{

int ip1;

int ip2;

intPair(int i1, int i2) : ip1(i1), ip2(i2) {}

intPair(int i1) : ip1(i1), ip2(i1) {}

};

// Calc sum (usinh value semantic)

Int Sum1(intPair p)

{

return p.ip1 + p.ip2;

}

// Calc sum (usinh ref semantic)

int Sum2(intPair &p)

{

return p.ip1 + p.ip2;

}

// Calc sum (usinh const ref semantic)

Int Sum3(const intPair& p)

{

return p.ip1 + p.ip2;

}

上面这个简单的struct,有三个Sum函数,作的事情完全一样,但是性能是否一样呢?我们用下面的程序来测试:

double Sum(int t, int loop)

{

using namespace std;

if (t == 1)

{

clock_t begin = clock();

int x =0;

for(int i = 0; i < loop; ++i)

{

x += Sum1(intPair(1,2));

}

clock_t end = clock();

return double(end - begin) / CLOCKS_PER_SEC;

}

else if (t == 2)

{

clock_t begin = clock();

int x =0;

intPair p(1,2);

for(int i = 0; i < loop; ++i)

{

x += Sum1(p);

}

clock_t end = clock();

return double(end - begin) / CLOCKS_PER_SEC;

}

else if (t == 3)

{

clock_t begin = clock();

int x =0;

intPair p(1,2);

for(int i = 0; i < loop; ++i)

{

x += Sum2(p);

}

clock_t end = clock();

return double(end - begin) / CLOCKS_PER_SEC;

}

else if (t == 4)

{

clock_t begin = clock();

int x =0;

intPair p(1,2);

for(int i = 0; i < loop; ++i)

{

x += Sum3(p);

}

clock_t end = clock();

return double(end - begin) / CLOCKS_PER_SEC;

}

else if (t == 5)

{

clock_t begin = clock();

int x =0;

for(int i = 0; i < loop; ++i)

{

x += Sum3(10);

}

clock_t end = clock();

return double(end - begin) / CLOCKS_PER_SEC;

}

return 0;

}

我们用了5个案列,对Sum1和Sum3 风别用了两种调用方式,对Sum2用了一种调用方式。我们测试了10万次调用:

double sec = Sum(1, 100000);

printf("Sum1 (use  ctor) time: %f \n", sec);

sec = Sum(2, 100000);

printf("Sum1 (use no c'tor) time: %f \n", sec);

sec = Sum(3, 100000);

printf("Sum2 time: %f \n", sec);

sec = Sum(4, 100000);

printf("Sum3 without conversion time: %f \n", sec);

sec = Sum(5, 100000);

printf("Sum3 with conversion time: %f \n", sec);

我们在VisualStidio 2010 中测试,结果是:

用例1    18ms

用例2    9ms

用例3    6ms

用例4    7ms

用例5    12ms

也就是说:用例1和5最慢,其他基本没有差别。

细心的读者不难看出,

1)用例5的性能问题,是因为Sum3用了C++的implicit conversion ,将整数自动转化成intPair 的临时变量。这是一个应用层面的问题,如果我们不得不将整数作这个转换,也就不得不付出这个性能上的代价。

2)用例1的问题和5类似,都是因为不得不每次创建临时变量。当然,可以强迫constructor inline 来使得临时变量的生成成本降低。

3)用例2用了在函数调用前了编译自生的copy constructor,不过因为 intPair object 很小,影响可以忽略不计了。

4)用例3性能是稳定的,但是它用了“间接”方式(详情请看我关于reference的博克),所以产生的指令比用例2多两条。但对性能的影响不大,估计和Intel的L1,L2 缓存有关。

*注意到OOP函数如果仅仅对 this 的成员存取数据,一般可以充分利用缓存,除非 object 过大。

5)用例4 和用例3生成代码完全一样,应该没有差别。const 只是编译时有用,生成的代码与const 与否无关。

性能问题的话题太多,本文只是蜻蜓点水,但是已经触及了C++的两个最大的性能隐患:

  a) 临时变量

  b) Implicit conversion (沉默转换)

2014-6-20 西雅图

C++ 性能剖析 (一)的更多相关文章

  1. 快速学习C语言二: 编译自动化, 静态分析, 单元测试,coredump调试,性能剖析

    上次的Hello world算是入门了,现在学习一些相关工具的使用 编译自动化 写好程序,首先要编译,就用gcc就好了,基本用法如下 gcc helloworld.c -o helloworld.o ...

  2. PDF.NET开发框架性能剖析

    PDF.NET开发框架性能剖析 前俩天发布了 关于PDF.NET开发框架对Mysql Sqlite PostgreSQL数据库分页支持的个人看法 ,说明了本人对框架的一些介绍和看法.今天我们一起思考一 ...

  3. ANTS Performance Profiler 8:支持对Web请求、异步代码和WinRT的性能剖析

    下载与激活:http://download.csdn.net/detail/lone112/6734291 离线激活   位于英国的Red Gate Software有限公司最近发布了ANTS Per ...

  4. MySQL性能剖析工具(pt-query-digest)【转】

    这个工具同样来自percona-toolkit 该工具集合的其他工具 MySQL Slave异常关机的处理 (pt-slave-restart)  验证MySQL主从一致性(pt-table-chec ...

  5. Python脚本性能剖析

    ################### #Python脚本性能剖析 ################### cProfile/profile/hotshot用于统计Python脚本各部分运行频率和耗费 ...

  6. Linux的系统级性能剖析工具-perf

    一直在找个靠谱且易用的性能分析工具,perf 貌似是很符合要求的,先给出阿里整理的几篇文档: Linux的系统级性能剖析工具-perf-1.pdf Linux的系统级性能剖析工具-perf-2.pdf ...

  7. golang 性能剖析pprof

    作为一个golang coder,使用golang编写代码是基本的要求. 能够写出代码,并能够熟悉程序执行过程中各方面的性能指标,则是更上一层楼. 如果在程序出现性能问题的时候,可以快速定位和解决问题 ...

  8. MySQL 服务器性能剖析

    这是<高性能 MySQL(第三版)>第三章的读书笔记. 关于服务,常见的问题有: 如何确认服务器是否发挥了最大性能 找出执行慢的语句,为何执行慢 为何在用户端发生间歇性的停顿.卡死 通过性 ...

  9. [windows操作系统]内核性能剖析

    profile这个词有(1)外形.轮廓.外观.形象(2)印象.形象(3)人物简介(4)剖面图.侧面图等意.在计算机和通讯协议中这个词也非常常见.这里主要介绍一下它在软件系统性能分析领域的一个释义. 翻 ...

随机推荐

  1. HDOJ(HDU) 2143 box(简单的多次判断-用的卫条件)

    Problem Description One day, winnie received a box and a letter. In the letter, there are three inte ...

  2. JavaScript 函数绑定 Function.prototype.bind

    ECMAScript Edition5 IE9+支持原生,作用为将一个对象的方法绑定到另一个对象上执行. Function.prototype.bind = Function.prototype.bi ...

  3. Apache-Tika解析JPEG文档

    通常在使用爬虫时,爬取到网上的文章都是各式各样的格式处理起来比较麻烦,这里我们使用Apache-Tika来处理JPEG格式的图片,如下: package com.mengyao.tika.app; i ...

  4. Corporate Identity - HDU 2328(多串求共同子串)

    题目大意:给你N(2-4000)个字符串,求出来他们的共同子串   分析:因为上次就说了再出现这种题就不用那种暴力的做法了,于是看了一些别的知识,也就是后缀树,把一个字符串的所有的后缀全部都加入字典树 ...

  5. weak属性需要在dealloc中置nil么?

    出题者简介: 孙源(sunnyxx),目前就职于百度 整理者简介:陈奕龙(子循),目前就职于滴滴出行. 转载者:豆电雨(starain)微信:doudianyu 不需要. 在ARC环境无论是强指针还是 ...

  6. [置顶] 【Git入门之十四】Git GUI

    原创作品,转载请标明:http://blog.csdn.net/jackystudio/article/details/12374495 文章都快写完了,Git GUI才浮出水面,很多人要骂我,这么方 ...

  7. SVN 基本操作

    SVN基础 一 简介 tortoiseSVN是windows下其中一个非常优秀的SVN客户端工具.通过使用它,我们可以可视化的管理我们的版本库.不过由于它只是一个客户端,所以它不能对版本库进行权限管理 ...

  8. 【C语言天天练(二一)】内联函数

            引言:调用函数时,一般会由于建立调用.传递參数.跳转到函数代码并返回等花费掉一些时间,C语言的解决的方法是使用类函数宏.在C99中,还提出了第二种方法:内联函数.         内联 ...

  9. [转] Are You Making a Big Mistake in This Volatile Market?

    Stock market volatility continues unabated. It may be too early to tell, but I’m marking the top of ...

  10. wpf的学习日志(二)

    window演示基础(windows presentation Foundation)用于windowsw图形显示系统 InitializeComponent()方法的工作就是system.windo ...