托管代码与非托管代码的执行效率比较
[转]托管代码与非托管代码的执行效率比较
值得一看
一、首先回答一个问题:托管代码(.net)比非托管代码(vc++)慢吗?
如果你用上面这个问题去问每一个人,基本上每个人都会回答,肯定会慢! 那么他们说的是正确的吗? 不,并不正确。 问题在于,绝大多数人认为.Net只是一个基于运行库的框架,就像Java或者VB,或者他们甚至以为.Net使用像Java一样的虚拟机系统。 他们并沒考虑到程序本身,沒考虑到程序是用来干什么,也没有考虑到访问网络或者磁盘的速度因素。简单来说,就是他们根本没有思考!
.NET并不像那种运行库(VB或者Java)。 它是一个经过精心构思的,并且微软在其身上下了极大功夫的框架,以保证它的良好运行。 在这篇文章 我将给大家展示一些将需要大量运算的代码,并且将他编译成托管以及非托管代码。 然后我将测量这两个库分别的表现。 你将看到, 并不会因为这是.net程序就自动要比c++程序慢,事实是,在某些情况,托管代码甚至比非托管代码更快。
基本上每个人都知道的是,所有.Net语言都将被编译成为一个叫做IL汇编的中间语言。但是计算机是如何执行这个中间代码的,却是很多人不知道,甚至理解错误了的。
JIT是.NET程序运行的重要部件之一,全称是即时编译器。我刚才说的误解,就是很多人(绝对不是少数,问了很多c++程序员,10个有9个这种想法)都以为JIT其实就是跟Java VM差不多的东西,是一个Interpreter,在运行时读取IL汇编代码,然后模拟成x86代码(也就是俗称的虚拟机)。但是事实上,.NET使用的是更为高级的技术。 .Net程序被加载入内存以后,当某段IL代码被第一次运行的时候,JIT编译器就会将这段IL代码,全部编译成本地代码,然后再执行。这也就是为什么.NET程序第一次运行都启动很慢的原因! 随.NET库,微软还附带了一个工具,可以事先将.NET程序所有的IL代码都编译成本地代码并保存在缓存区中,这样一来,这个程序就跟c++编译的一模一样了,没有任何区别,运行时也可以脱离JIT了(这里不要混淆了,这里不是说可以脱离.NET库,而是说不需要在进行即时编译这个过程了)。所以,请不要将.NET和Java混为一谈,两个的运行效率根本不是一个等级的!
二、实验比较
作为测试算法,我们选中了FFT(Fast Fourier Transform),这是一个将跟时间有关系的数据(例如音乐)转换成他应有的频率信息的算法。
这个算法有很多种,如果你用Google搜索会发现很多,这里我选中了Real Discrete Fourier Transform, 因为他比较简单明了,比较好修改。 我将其复制了4份,分别用于测试托管的C++, C++/CLI,C#。
非托管的代码我只是将其函数名称改成了fourier,并且加入了__declspec(dllexport)用来导出。
托管的代码改动的要稍多些:
* 方法参数改成了托管的Array, 并且使用Array::Length来代替额外的长度参数
* 涉及到三角函数的地方都改为使用Math类下的方法
* 算法被作为一个公开类的静态成员导出
然后我把托管的c++代码转换成了c#,只做了极小的变化(大多数是语法上以及申明上的改动)
最后,我又将托管的c++代码转换成了C++/CLI
然后我们将所有版本都分别编译几个不同的版本:未优化版,空间优化,速度优化.
结果:
我将这些程序分别在两台电脑上进行了测试,一台是装了.net 2.0的 XPSP2,处理器是PIII 850, 512MB内存。 另外一台是Vista build 5321,处理器是2GHz 移动PIV,1G内存,每次测试我都是取100次算法运算的平均值,结果单位是毫秒,以下是PIII电脑的运行结果:
沒优化 进行了空间优化 进行了速度优化
Unmanaged 92.88 ± 0.09 88.23 ± 0.09 68.48 ± 0.03
Managed C++ 72.89 ± 0.03 72.26 ± 0.04 71.35 ± 0.06
C++/CLI 73.00 ± 0.05 72.32 ± 0.03 71.44 ± 0.04
C# Managed 72.21 ± 0.04 69.97 ± 0.08
PIV电脑的结果:
沒优化 进行了空间优化 进行了速度优化
Unmanaged 45.2 ± 0.1 30.04 ± 0.04 23.06 ± 0.04
Managed C++ 23.5 ± 0.1 23.17 ± 0.08 23.36 ± 0.07
C++/CLI 23.5 ± 0.1 23.11 ± 0.07 23.80 ± 0.05
C# Managed 23.7 ± 0.1 22.78 ± 0.03
可以看出,非托管代码在不同的优化方案上存在很大的效率差异,PIII上不优化比优化慢35%,在PIV上也是。 在这个简陋的统计上表明,不管是哪种优化方案,管理代码在运行效率上并没有太大区别,编译器和连接器并没有影响到运行效率太多, 我在后面会说更多关于这方面的信息。
奇怪的是,在Vista下,管理代码进行空间优化甚至比进行速度优化速度更快!
C#的结果跟托管的C++比起来,并没有太大区别,但是可以看到, 优化过的c#代码比优化过的托管C++代码要稍快些。
现在来比较以下托管代码和非托管代码的结果。 在不优化的情况下,托管代码远远快于非托管代码,这个差距在优化空间后被稍微缩短了点,只有在进行速度优化后,非托管代码才比托管代码稍稍快上一点。非托管代码和C#代码的差别只有3%左右,不过,c#代码仍然比c++的更快!
三、机制实质
.NET的编译器(在这个情况下是托管的C++代码) 可以看成是与非托管C++编译器的Parser引擎是等价的。编译器将生成类,方法等的表,然后进行了一系列的高等级优化。 .NET真正的非托管编译器其实是JIT(即时编译器):这才是程序真正转换成低等級的x86代码的地方..NET编译器和JIT编译的组合,其实跟非托管C++编译器等价的,唯一的不同是,.Net被分成了两个部分.事实上,JIT在运行托管代码时,对.NET程序针对客户电脑进行了优化,而不是像非托管代码那样是在程序员电脑上进行的优化。结果表明,托管C++代码和c#代码的优化设置带来的影响非常小。 显而易见的是,C#代码至少是跟C++代码同样高效。
记住!在.NET中没有任何一个部分是自动就必C++代码慢的,运行效率完全取决于程序员。任何一个告诉你托管代码比非托管代码慢的人,都是没有考虑到.NET运行机制的人,简单的说,就是对.NET一窍不通!
个人认为C++的编译(了解c++编译原理的都知道)在生成中间代码或汇编代码过程中,可能涉及到优化处理。优化有两种:一种优化仅涉及代码本身,主要是删除公共表达式、循环优化、代码外提、无用 代码赋值等。另一种优化设计具体的计算机硬件,比如,如何根据机器硬件执行指令的特点对指令进行调整优化,减少目标代码长度,提高执行效率。这是非托管代码,这样带来的一个不好的地方就是对于不同很多硬件的用户端,你要达到最好优化效率必须在不同编译环境编译不同版本
但是对于c#的来说,JIT编译则会自动根据硬件的环境去优化编译IL中间代码(其实就是汇编代码)。