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类似,都是因为不得不每次创建临时变量。

3)用例2用了VC++的input argument optimization 优化,免于copy constructor 的使用,不过我不知道是否所有的编译都用了这个优化。这个优化使得用例2性能不差与用例3多少。

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

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

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

  • 临时变量
  • Implicit conversion (沉默转换)

2014-6-20 西雅图

C++ 性能剖析 (一),布布扣,bubuko.com

时间: 2024-10-01 06:58:36

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

性能剖析器

 PerformanceProfiler.h: #include<iostream> using namespace std; #include<map> #include<windows.h> #include<time.h> #include<string> #include<assert.h> #include<stdarg.h> #include<thread> #include<mutex>

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

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

MySQL之服务器性能剖析

关于mysql服务器性能,可能需要关注的点会比较多,如:如何确认服务器是否达到了性能最佳的状态,找出某条语句为什么执行不够快,以及诊断被用户描述成“停顿“,“堆积“或者“卡死“的某些间歇性疑难故障.这看起来很不简单.但是事实证明,有一个简单的方法能够从噪声中发现苗头. 这个方法就是专注于测量服务器的时间花费在哪里,使用的技术则是性能剖析. mysql性能的问题,可能不同人会有不同的说法.例如:每秒查询数,cpu利用率,可扩展性  等等. 每个人在不同场景对性能有不同的理解. 但在这我们将给出一个

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

下载与激活:http://download.csdn.net/detail/lone112/6734291 离线激活 位于英国的Red Gate Software有限公司最近发布了ANTS Performance Profiler 8 Beta,支持对Web请求.异步代码和Windows商店应用的性能剖析.该版本还支持SharePoint 2013和一个新的时间线,这使开发者不但能够监控应用程序的性能,还能深入到想要检查的具体区域. Web请求剖析使开发者能够捕获向外的HTTP请求,其中包括请求

【性能分析】初探VS2010中的Profile(性能剖析)功能

VS2010中的Profile(性能剖析)功能十分实用,它可以协助程序员迅速锁定当前程序的性能瓶颈,为程序的优化做准备. 点击VS2010的Analyze菜单,选择Launch Performance Wizard,依照提示可以很方便地完成Profile的必需配置.可能你会碰到"Injection of runtime library failed"错误,这时就不要使用Wizard,而要选择Analyze下拉菜单中的Profiler->Attach/Detach...,将Prof

Python脚本性能剖析

################### #Python脚本性能剖析 ################### cProfile/profile/hotshot用于统计Python脚本各部分运行频率和耗费时间等统计信息.pstats可用于格式化这些信息 cProfile,属C扩展.开销较小,适合剖析长时间执行的Python程序,推荐使用此模块 profile.纯Python模块,存在明显开销,但想对其扩展的话相对照较easy hotshot,实验性的C模块.主要关注开销最小化,现已不再被维护将来可能

服务器性能剖析(profiling)之——简介

性能剖析(profiling)是专注于测量服务器时间花费在哪里的一种技术,这里"性能即响应时间". 测量是一项很有挑战性的工作,并且分析结果也同样有挑战性,测出时间花在哪里,和知道为什么花在那里是两码事-- 性能剖析一般有两个步骤: 测量任务所花费的时间: 对结果进行统计和排序,将重要的任务排到前面. 关于性能剖析报告: 任务名.任务的执行时间.任务的消耗时间.任务的平均执行时间.该任务执行时间占全部时间的百分比. 性能剖析报告会按照任务的消耗时间进行降序排序. 有两种比较常见的情况会

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

上次的Hello world算是入门了,现在学习一些相关工具的使用 编译自动化 写好程序,首先要编译,就用gcc就好了,基本用法如下 gcc helloworld.c -o helloworld.o helloworld.c是源码,helloworld.o是编译后的可执行文件,运行的话就用 ./helloworld.o就可以了. 但是如果代码写的多了,每次改动完都手动用gcc编译太麻烦了,所以要用Makefile来 自动化这项工作,在当前目录下创建Makefile文件,大概如下 hellowor

【MySQL】《高性能MySQL》 学习笔记,第三章,服务器性能剖析

第三章:服务器性能剖析 ? 本章将针对如下三个问题进行解答: ? 如何确认服务器是否达到了性能最佳的状态 ? 找出某条语句为什么执行不够快 ? 诊断被用户描述成"停顿","堆积","卡死"的某些间歇性疑难故障 1.性能优化简介: ? 针对性能问题,1000个DBA,有1000个回答.诸如,"QPS","CPU Load","可扩展性"之类. 原则一:我们将性能定义为完成某件任务所需要的时