C++ 性能剖析 (一)
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++ 性能剖析 (一)的更多相关文章
- 快速学习C语言二: 编译自动化, 静态分析, 单元测试,coredump调试,性能剖析
上次的Hello world算是入门了,现在学习一些相关工具的使用 编译自动化 写好程序,首先要编译,就用gcc就好了,基本用法如下 gcc helloworld.c -o helloworld.o ...
- PDF.NET开发框架性能剖析
PDF.NET开发框架性能剖析 前俩天发布了 关于PDF.NET开发框架对Mysql Sqlite PostgreSQL数据库分页支持的个人看法 ,说明了本人对框架的一些介绍和看法.今天我们一起思考一 ...
- ANTS Performance Profiler 8:支持对Web请求、异步代码和WinRT的性能剖析
下载与激活:http://download.csdn.net/detail/lone112/6734291 离线激活 位于英国的Red Gate Software有限公司最近发布了ANTS Per ...
- MySQL性能剖析工具(pt-query-digest)【转】
这个工具同样来自percona-toolkit 该工具集合的其他工具 MySQL Slave异常关机的处理 (pt-slave-restart) 验证MySQL主从一致性(pt-table-chec ...
- Python脚本性能剖析
################### #Python脚本性能剖析 ################### cProfile/profile/hotshot用于统计Python脚本各部分运行频率和耗费 ...
- Linux的系统级性能剖析工具-perf
一直在找个靠谱且易用的性能分析工具,perf 貌似是很符合要求的,先给出阿里整理的几篇文档: Linux的系统级性能剖析工具-perf-1.pdf Linux的系统级性能剖析工具-perf-2.pdf ...
- golang 性能剖析pprof
作为一个golang coder,使用golang编写代码是基本的要求. 能够写出代码,并能够熟悉程序执行过程中各方面的性能指标,则是更上一层楼. 如果在程序出现性能问题的时候,可以快速定位和解决问题 ...
- MySQL 服务器性能剖析
这是<高性能 MySQL(第三版)>第三章的读书笔记. 关于服务,常见的问题有: 如何确认服务器是否发挥了最大性能 找出执行慢的语句,为何执行慢 为何在用户端发生间歇性的停顿.卡死 通过性 ...
- [windows操作系统]内核性能剖析
profile这个词有(1)外形.轮廓.外观.形象(2)印象.形象(3)人物简介(4)剖面图.侧面图等意.在计算机和通讯协议中这个词也非常常见.这里主要介绍一下它在软件系统性能分析领域的一个释义. 翻 ...
随机推荐
- 在Linux下查看环境变量
原文地址:http://blog.chinaunix.net/uid-25124785-id-77098.html 有时候在编写makefile的时候,自己都不清楚有些变量是什么,也不清楚如何查看,于 ...
- ActionScript接收socket服务器发送来的数据
原文地址:http://www.asp119.com/news/2009522181815_1.htm 从socket中接收数据的方法取决于你使用socket类型,Socket和XMLSocket都可 ...
- 关于Linux
这是一个2B让我写的关于Linux的一点东西. 其实我对Linux一直都是持有一种很尊敬的态度,作为一个非商业性的操作系统,能够成长成这样简直是不可思议,有一种Dota在游戏界的感觉,很让人佩服.但是 ...
- Struts2 入门(新手必看)
船舶停靠在港湾是很安全的,但这不是造船的目的 Struts 2及其优势 Struts 2是一个MVC框架,以WebWork框架的设计思想为核心,吸收了Struts 1的部分优点 Struts ...
- Struts2.xml中result type属性说明
在struts2配置XML里,result中type属性有以下几种: 1.dispatcher:服务器跳转到前台,后面跟着可以是JSP.htm等等前台页面,默认是这种. 2.redirect:客户端跳 ...
- 全面剖析XML和JSON
1.定义介绍 (1).XML定义扩展标记语言 (Extensible Markup Language, XML) ,用于标记电子文件使其具有结构性的标记语言,可以用来标记数据.定义数据类型,是一种允许 ...
- Linux - 标准输入转换为标准输出 代码(C)
标准输入转换为标准输出 代码(C) 本文地址:http://blog.csdn.net/caroline_wendy Linux能够使用getc()和putc(),读取和写入每个输入字符. 代码: / ...
- 关于cocostudio加载UI json CCUIHELPER未声明问题
查看官方的文档,在文档的最后添加了如何加载项目.如下代码: UILayer* ul =UILayer::create(); ul->addWidget(CCUIHELPER->create ...
- 【转】iOS中流(Stream)的使用
转自:http://southpeak.github.io/blog/2014/07/17/ioszhong-liu-stream-de-shi-yong/流提供了一种简单的方式在不同和介质中交换数据 ...
- Day8 - Python网络编程 Socket编程
Python之路,Day8 - Socket编程进阶 本节内容: Socket语法及相关 SocketServer实现多并发 Socket语法及相关 socket概念 socket本质上就是在2台 ...