首页 > 编程语言 > 详细

C++ 性能剖析 (一)

时间:2014-08-21 05:14:44      阅读:321      评论:0      收藏:0      [点我收藏+]

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

C++ 性能剖析 (一)

原文:http://www.cnblogs.com/ly8838/p/3926236.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!