Chinaunix首页 | 论坛 | 博客
  • 博客访问: 289353
  • 博文数量: 111
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 816
  • 用 户 组: 普通用户
  • 注册时间: 2014-05-04 20:35
文章分类

全部博文(111)

文章存档

2016年(1)

2015年(5)

2014年(105)

我的朋友

分类: C/C++

2014-08-11 17:51:48

Robert C. Martin博客中文版有一篇文章,比较了Java, C/C++和Ruby的性能。
看过之后有些不解,于是作了一些测试。在此写一下我的心得体会。

原文地址:
http://blog.csdn.net/rmartin/archive/2006/08/30/1143161.aspx


我这里没有ruby和Java,因此用C#替代,C#的效率应该与Java相当。
原文有一处Bug,我做了一下修改,并且把计时代码提出到循环外。
为了不打断文章,修改后的代码代码附在本文结尾。

经测试,C++和C#的执行性能极为相近,大约在4.3到4.4秒。C++的
平均性能要略高。

接下来,我们必须要查找到影响性能的关键代码。很明显是这一段:
    for (int j=2*n; j         sieve[j] = false; 
经测试,这段代码至少消耗了90%的时间。

下面比较一下产生的汇编代码:
C++:
00401049  mov         byte ptr [eax+esi],0 
0040104D  add         eax,ecx 
0040104F  cmp         eax,edi 
00401051  jl          generate+49h (401049h)

C#:
000000b4  cmp         ebp,dword ptr [esi+4] 
000000b7  jb          000000BE 
000000b9  call        79313459 
000000be  mov         byte ptr [esi+ebp+8],0 
000000c3  add         ebp,edi 
000000c5  cmp         ebp,ebx 
000000c7  jl          000000B4

可以看出,C++的代码更加精减。C#由于要进行运行期的检查,要多
产生一些代码。

那么为什么如此高度优化的C++代码却和C#的效率相差无几?初步猜测,
这与处理器高速缓存的命中率有关。
于是针对这个思路,做了新的测试,减小使用内存的尺寸。获得的执行
时间如下:
C++:453ms
C#:625ms
可以看出,性能的差别还是很明显的。

经过多次调整数据,可以发现,内存超过1M的时候,两种语言的效率
就会非常接近。由此可见,优化代码除了考虑代码本身的效率,还要
考虑缓存的问题。

有兴趣的话可以测试一下这两段代码:
条件一的情况:C++:7.5s  C#:20.2s
另外一种情况:C++:1.82s  C#:2.00s
测试结果可以说明缓存所带来的问题。注意,跟内存分配关系不大的,
即使我把C#的“bool[] p = new bool[i];”提出来,也要耗时14s多。
// C++
int main(int ac, char** av)

    unsigned time = GetTickCount(); 
#if 1
    for(int i = 100000; i < 200000; i += 1)
#else
    for(int i = 1000000; i < 2000000; i += 1000)
#endif
    {
        bool* p = new bool[i];

        for(int j = 0; j < i; j += 2)
        {
            p[j] = false;
        }

        delete [] p;
    }
    time = GetTickCount() - time;
    cout << time << endl; 
}
// C#
        static void Main(string[] args)
        {
            DateTime start = DateTime.Now;
#if true
            for(int i = 100000; i < 200000; i += 1)
#else
            for(int i = 1000000; i < 2000000; i += 1000)
#endif
            {
                bool[] p = new bool[i];

                for (int j = 0; j < i; j += 2)
                {
                    p[j] = false;
                }
            }
            TimeSpan c = DateTime.Now - start;
            System.Console.WriteLine(c.TotalMilliseconds);
        }


在此顺便说一下我对C++和C#的看法。

首先,必须强调,两种语言都是非常优秀的,并且都有其优势领域。

其次,C#效率很高,用得好的话,的确有可能超过C++程序的效率。有兴趣
的话,大家可以比较一下Apache的xerces xml和.net 2.0的xml,经我测试,
.net 2.0的效率要高一些。

再次,C/C++的效率是最高的(C和C++的代码效率没什么差别,看看生成
的汇编代码就知道了),但是也要看你怎么写程序。你可以写出效率很差
的程序,比如TinyXml,但是也有可能由此获得一定的好处(TinyXml真的
很小)。你也可以写出效率非常高的程序,我曾经写的一个xml解析器效率
是.net 2.0的两倍多,不过也因此去掉了一些xml中不常用的特性。

下面比较一下C#和C++的不同,借此再讨论一下效率的差别。
基于.net framework的程序都有类似的特点,因此我们可以用C#作为其
代表语言来讨论。
C#有以下几点有别于C++的地方:
1:JIT。C#或者其它的.net语言都会被编译成MSIL,运行时才会被编译成
本地代码。这会导致两个坏处:启动速度慢,和优化不完全。不过也有可
能获得一定的好处,比如针对处理器进行特殊的优化,抛弃不用的代码以
减少内存使用等。总的来说,JIT是会降低效率的。微软提供了ngen来生成
本地代码,效率提升很明显,尤其是启动速度和内存用量上的变化。
2:垃圾收集。GC的分配速度很快,这一点要比C++的缺省的new高效。
由于有GC存在,无需维护引用计数,资源的共享变得很容易,这也会简化
代码和提高效率。
不过Collect的时候还是会耗费一些时间。而且大量垃圾内存驻留着,使得
内存用量居高不下。无论是Java还是C#程序,内存用量都比C++要多。
C++可以使用内存池来优化内存的分配,效率会有很大提高。未经优化
的C++程序有可能在这方面极其低效,尤其是某些滥用STL的程序。不了解
底层的实现而去随意使用STL容器会在无意之中就耗尽了CPU资源,隐含的
拷贝构造多数时候就是罪魁祸首。C#和Java就没有这方面的问题。
3:RTTI。强大的RTTI可以使我们很容易地设计某些功能,但是注定会丧失
一些效率。C++里,多数时候使用static_cast就可以解决问题。即使是需要
动态的地方,也可以有一些特殊的解决办法。RTTI和效率就是鱼与熊掌,看
你如何权衡了。
4:指针。C#程序不常用指针,虽然可以使用unsafe代码,但是仔细推敲一
下它生成的最终代码,会发现还不如不用。C#的指针的最大作用应该是使用
这个东西来调用C/C++的函数。
灵活使用指针可以减少数据的拷贝,可以生成效率非常高的代码,也可以
简化代码。C#里必须要使用Marshal来实现这些功能,其实挺麻烦,也低效。
5:Runtime Check。这个就不用多说了,本文所附的程序就是活例子。
Runtime Check与效率也是鱼与熊掌,需要权衡。

总之,使用什么语言,还要看你打算做什么东西。我们必须要客观地了解
各种语言的优缺点,才能做出正确的选择。

// C++

#include  
#include  
#include

using namespace std;

void generate(int max)

    bool *sieve = new bool[max];
    memset(sieve, 1, max*(sizeof(bool)));

    sieve[0] = false; 
    sieve[1] = false; 
    double maxsqrt = sqrt((double)max); 
    for (int n=2; n     { 
        if (sieve[n])
        { 
            for (int j=2*n; j                 sieve[j] = false; 
        } 
    }

    delete[] sieve;
}


int main(int ac, char** av)

    unsigned time = GetTickCount(); 
#if 0
    //for (int i = 1000000; i < 2000000; i += 10000)
    for (int i = 10000; i < 20000; i += 1)
    {
        generate(i); 
    }
#else
    for (int i=100000; i<=5000000; i+=100000)
    { 
        generate(i); 
    } 
#endif
    time = GetTickCount() - time;
    cout << time << endl; 
}

// C#
using System;
using System.Collections.Generic;
using System.Text;

namespace ConsoleApplication1
{
    class Program
    {
        public static unsafe void generate(int max)
        {
            Boolean[] sieve;
            sieve = new Boolean[max];
            for (int i = 0; i < max; i++)
                sieve[i] = true;
            sieve[0] = false;
            sieve[1] = false;
            int maxsqrt = (int)Math.Sqrt(max);
            for (int i = 2; i < maxsqrt; i++)
            {
                if (sieve[i])
                {
                    for (int j = 2 * i; j < max; j += i)
                    {
                        sieve[j] = false;
                    }
                }
            }
        }
        
        static void Main(string[] args)
        {
            DateTime start = DateTime.Now;
#if false
            //for (int i = 1000000; i < 2000000; i += 10000)
            for (int i = 10000; i < 20000; i += 1)
            {
                generate(i); 
            }
#else
            for (int i = 100000; i <= 5000000; i += 100000)
            {
                generate(i);
            }
#endif
            TimeSpan c = DateTime.Now - start;
            System.Console.WriteLine(c.TotalMilliseconds);
        }
    }
}

阅读(2222) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~