Rumah > Artikel > pembangunan bahagian belakang > C++ 性能剖析 (一),性能剖析_PHP教程
性能问题也不是仅仅用“技术”可以解决的,它往往是架构,测试,假设等综合难题。不过,对于一个工程师来说,必须从小做起,把一些“明显”的小问题解决。否则的话积小成多,千里堤坝,溃于蚁穴。
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
{
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
{
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
{
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
{
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
{
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 西雅图
没时间帮你写程序需说个思路就闪人了 你先把3种排序的书上的例子抄下来 写成3个函数, 每个函数里面开头和结尾取一次系统时间 最后相减得出开销时间 在打印出来
我把网上的程序修改了一下,并整合了,你看看
#include
#include
#include
#define M 50
#define MAX 100000;
typedef struct
{
int weight;//结点权值
int parent,lchild,rchild;
}HTNODE,*HUFFMANTREE;
typedef char** HUFFMANCODE;//动态分配数组存储哈夫曼编码表
typedef struct
{
int key; /*关键字*/
}RecordNode; /*排序节点的类型*/
typedef struct
{
RecordNode *record;
int n; /*排序对象的大小*/
}SortObject; //待排序序列
HUFFMANTREE huffmantree(int n,int weight[])//构建哈夫曼树
{
int m1,m2,k;
int i,j,x1,x2;
HUFFMANTREE ht;
ht=(HUFFMANTREE)malloc((2*n)*sizeof(HTNODE));
for(i=1;i{
ht[i].parent=ht[i].lchild=ht[i].rchild=0;
if(iht[i].weight=weight[i];
else
ht[i].weight=0;
}
for(i=1;i
m1=m2=MAX;
x1=x2=0;
for(j=1;j{
if((ht[j].weight
m2=m1;
x2=x1;
m1=ht[j].weight;
x1=j;
}
else if((ht[j].weight
m2=ht[j].weight;
x2=j;
}
}
k=n+i;
ht[x1].parent=ht[x2].parent=k;
ht[k].weight=m1+m2;
ht[k].lchild=x1;
ht[k].rchild=x2;
}
return ht;
}
void huffmancoding(int n,HUFFMANCODE hc,HUFFMANTREE ht,char str[])
{
int i,start,child,father;
char *cd;
hc=(HUFFMANCODE)malloc((n+1)*sizeof(char*));//分配n个字符编码的头指针
cd=(char*)malloc(n*sizeof(char));//分配求编码的工作空间
cd[n-1]='\0';//编码结束符
f......余下全文>>