CSDN博客

img xujinsong

C# 与 C 、 C++ 、 D 、 Java 的性能比较(三)

发表于2004/7/17 22:22:00  2885人阅读

分类: JAVA中心

C# 与 C 、 C++ 、 D 、 Java 的性能比较(三)

(文章转自http://mag.vchelp.net)jason

f2i 抛开Intel不谈,可以看出C#在浮点到整型的转换中效率远远高于其余四个,仅是C,C++, 和D开销的2/3,是Java的2/5。在我让它们使用相同的随机数发生器前,不同语言的性能差异极大,这说明转换速度非常依赖具体的浮点数值,当然这是后见之明了。为了确保每个语言做相同的转换,程序计算转换值的总和,这也有助于任何过度的优化去除整个循环。可以得知这种转化在语言间正确度不同。每种语言的计算总和迭代10,000次的结果在表3中列处。它们表明(虽然无法证实)C,C++,和D是最正确的,Java其次,C#最差。C#牺牲正确性和降低处理器浮点精度(参见 引用)换取优越性能是令人信服的,但事实上做此判断前我还需要更多的证据。至于Intel,说它的性能没有什么更卓越的也是公平的。


5个语言这里效率相当。另4种性能比C#低5%以内。唯一叫人感兴趣的是这四个语言都是性能比C#略好。这表明C#确实效率低,虽然在这方面只低一点。这与从整型到浮点型转换的结果相反。同样,值得我们注意的是,虽然Java在浮点到整型的转换中性能很低,但在整型到浮点的转换中却是最好的,比C#高10%。对f2i,Intel表现极佳,其C和C++分别只用了C#时间的10%和23%。

i2str 整型到字符型的转换。 C#比C和D的printf略好。比C++的以iostream为基础的转换效率要高。它是C#运行时间的4倍(对Intel 和VC6 STL/CRT是5倍)。但Java性能出色,用了C#2/5的时间。因为C++的效率低下,第二个变量使用STLSoft的 integer_to_string<> 模板函数。它在很多编译器上比C和C++库的所有可利用的转换机制要快。在这种情况下,性能比C#高10%,比C和D高约20%。但是因为Intel编译器的优化影响, integer_to_string<> 似乎找到了其完美搭档:其时间比C#低30%,是第三个速度超过Java的,比Iostream快17倍以上。认为它很可能已经接近了转换效率的上限也是合理的。

str2i 字符型到整型的转化结果与前面大相径庭。 C和D用atoi比C#快约5-8倍,比Java快3倍,Java比C#效率高得多。但是这四个比C++以iostream为基础的转换都快。C++用 Digital Mars/STLPor运行时间t 是 C#的2.5倍,用 Intel和VC6 CRT/STL 是 10倍。这是iostream的另一个致命弱点。但因此说C++效率低下未免有失公允(实际上,我正在为 STLSoft 库写字符至整型的转换,以和它 integer_to_string<> 的杰出转换性能相匹配。最初的结果表明它将优于 C的atoi,所以这个转换中C++也是优于C的。我希望能在本文刊发前完成此转换。请参阅 http://stlsoft.org/conversion_library.html 。 )

picalc 在这里各语言性能表现相近。 C#和Java几乎相同,比C,C++效率高上10%。C#的性能可以这样解释:它的浮点数操作效率高,这可以从f2I看出。但这不能理解Java的表现。我们可以认为C#和Java都能较好的优化循环,它涉及函数调用,但这有赖于深入研究。既然性能相差10%以内,我们只能得出这样的结论:各语言在循环结构性能方面差异不显著。有趣的是,Intel的优化这里没有实际效果,可能是因为Pi值计算太简单了吧。

Picalcr : 这里的结果可以更加确定一点, C , C++ , C# 和 D 语言的性能偏差在 2% 之内,我们可以公平地说它们对于递归执行的性能是一样的。 Java 却是花费比其他四种语言多 20% 的时间,加上 JVM ( Java 虚拟机)在超过 4500 次递归时堆栈耗竭,可以明显地从速度和内存消耗方面得出 Java 处理递归不是很好。 Intel 的优化能力在这里有明显的作用(让人印象深刻的 10% ),但我在这里并没有详细分析这一点。

Sieve: 相对简单的计算质数的算法(仅包含迭代和数组访问)说明 C , C++ , C# 和 D 语言在这方面事实上是一样的(仅 0.5% 的偏差),但 Java 的性能低了 6% 。我认为这中间大有文章, C , C++ 代码不进行数组边界安全检查,而 C# 和 Java 进行数组边界检查。我对这种结果的解释是: C# 和 D 语言在 FOR 循环内能根据对数组边界的条件测试对边界检查进行优化,而 Java 不行。由于 C# 是新兴语言, D 语言还没发行,这就让人不那么失望了。即使这不是为 Java 辩解,也不会让人印象深刻了。再说, Intel 优化起了明显的作用——增长 5% 性能,正如 picalcr 例子,这相对于其他语言更让人印象深刻。

Strswtch : 这个测试对于 C# 语言不是很好。即使是 C++ 低效的字符串类操作符 = = 都比 C# 的性能快 2.5 倍 (Digital Mars) 到 5 倍( Intel VC6 STL/CRT )。 .NET 环境下字符串是内部处理的,这表示字符串存储在全局离散表中,副本可以从内存中消除,等值测试是以一致性检查的方式进行的 ( 只要两个参数都是在内部 ) 。这明显表示内部修复机制的效率严重低下或者一致性检查没有建立 C# 的 ”string-swatching” 机制性。 C# 的这种严重低效率更让人倾向于后者,因为想不到个好的理由解释为什么会这样。正如例 8 所示,变量 1 的字符串实例是文字的,都在内部(事实上,这里有个限制没有在例 8 中表示出来,在变量 1 的预循环中用来验证参数是真正在内部的)。

当在变量 2 中假装进行一致性检查,发现仅 Java 的性能有明显的提高,从 2% 到令人印象深刻的 30% 。很明显 Java 在使用一致性检查所取的性能是不切实际的,因为在真正的程序中不能限制字符串都是文字的。但这可用来说明支持内部处理和提供可编程实现访问这种机制的语言的一种性能。讽刺的是, C# 作为这五种语言之一,它的性能是最差的。

Strcat : 表 2 以微秒( μs )显示了五种语言每一种在这个例子中的四个变量的花费时间。本文没有足够的篇幅来细节描述 17 执行代码,但尽量保持例子 7 的四个变量的真实面貌。变量 1 和 2 在所有语言执行代码中涉及以堆栈为基础的字符串内存,同时 C , D 语言在第三个变量中、 C , C++ , D 语言在第四个变量中用结构内存,这在某种程度说明他们的性能优于其他变量和语言的执行代码。本例子的 C , C++ 的讨论适合于 Digital Mars 版本,因为很明显的 Intel 所带来的性能提高被 Visual C++ 运行时刻库的效率(非期望)所抵消,事实上 Intel 的每种测试的性能都比 Digital Mars 的差。

第一种测试是以默认的方式执行的,很显然 C# 和 D 语言的性能都好于其它语言。很奇怪的是 Java 的性能最差,因为 Java 被认为有能力在一条语句中把字符串序列解释成字符串构件格式(在变量 4 中手工转换)。

第二种测试,众所周知它的格式不好,透露了很多东西。它的劣性根据语言而不同,对于 C# , D 和 Java ,包含在连续的表达式中连接每一项,而不是象第一个变量在一个表达式中。对于 C ,它简单地省略了在开始多项连接重新分配之前的的内存请求。对于 C++ ,它使用 strstream 和插入操作符而不是使用 str::string 和 + ()操作符。结果显示所有语言不同程度地降低性能。 C 内存分配器的附加轮询看起来没有起多大作用,可能因为没有竞争线程造成内存碎片,所以消耗同样的内存块。 C++ 改变成 iostreams 使代码更紧凑,但没有转变数字部分,它们仅被插入到流当中,所预计的执行结果验证了这一点。 D 因为这个改变降低性能许多( 32% ), Java 更多( 60% )。很奇怪的是 C# 性能在这里仅降低很小,少于 7% 。这让人印象非常深刻,意味着可能在 .NET 的开发环境下因为 Java 字符串连接而形成的代码回查的警觉本能会衰退。

第三种测试使用 printf()/Format() 的变量不足为奇。通过使用部分 / 全部的帧内存, C , C++ 和 D 达到很好的性能。奇怪的是 C# 的性能仅是 22% ,简直不值一提。 Java 根本没有这个便利功能。(以个人的观点也是不值一提的) C#和D语言都能够在封闭的循环中,依照数组边界测试情况,优化他们的越界检查。

最大的 测试 -硬编码的性能-这个是很有趣的问题,因为 C++还是能提供超级的性能,如果能够依照问题的精确领域来编码,即使别的语言(C#,java)使用默认的或者性能优化的组件或者理念也是如此。在C#和java上使用他们各自的StringBuilders能够提供真实的效果,达到他们在这个集合中的最佳性能。然而,C#能发挥最佳效率的机制,还是不能与C/C++、D语言相比。更糟糕的是,java的最佳性能都比别的语言的最差性能要可怜,这是相当可悲的。

总之,我们说对底层直接操作的语言垂手可得地赢得那些转化率工作得很好的或者完全地在中间代码中,并且很显然地能够提供良好性能的语言用法对java是很重要的而对C#是不太重要的。请注意这一点。

Strtok . Table3显示了五种语言的每一个环节总共的时间(单位是毫秒ms)。同时他们的空间占用在这篇文章中并没有提及,但是所有语言的实现都是非常直接的并且很明显很接近在Listing9中列出的四个问题的本质。与strcat一样,基于同样的原因,使用Intel编译器的结果没有讨论。

第一个问题――分割字符,保留空格――显示C++版本是明显的胜利者,D语言要慢20%,C# 要3倍的时间。显然,这个与库的特征的关系比语言的更明显,

并且我使用 tokenizer比著名的Boost更多一些,那样的话,D语言要比C++快2倍,并且C#只慢20%。虽然如此,STLSoft的tokenizer是免费得到,我想我们应该把这个作为C++的一个胜利。(其实,template在这一点上的使用STL的basic_string<char>作为他的值类型,这个并不因为他的良好性能而闻名,这可能引起争论-我并没有使用一个更有效率的string,就像STLSoft的frame_string.总之,我认为这是个公平比较)。

第二个问题--分割字符,去掉空格--包含所有语言的实现版本。自然地,C语言赢得了比赛,它使用了strtok()函数,这个函数在创建tokens时并不分配内存并且直接写终结符NULL到tokenized string的结尾。尽管有这些不利条件,C++的性能还是很好的,时间是C的221%,比较好的是D语言,432%。

C#和java就很差了,分别是746%和957%。这简直是不敢相信的,C#和java运行的时间是C++的3.4倍和4.3倍,这三种语言都为tokens分配了内存。我相信这个是STL模型在处理iterable sequences时比passing around arrays更有效率的很好的例子。(请注意,我们有高度的信心这是场公平的关于C++,C#和D语言的比较,我写了三种tokerizer的实现,都是公开的,可得到的.)

第三个问题--分割句子,保留空格--显示了两件事情。第一,在这三种语言在实现这个程序的代价比实现单个字符更昂贵。第二,我们发现D语言取代了C++的最佳性能的地位,领先5%左右。C#继续被甩下,大概是另外两种语言的1.8倍时间左右。

第四个问题--分割句子,去掉空格--没有任何令人意外的,C++和D拥有大概一致的性能。C#比较不错(大概2倍的性能差距)java更慢(差不多3倍时间)。对于C++,C#和D来说去掉空格比不去掉空格导致一小点的性能损失。很明显的,在C#中,这么做比其他两个的损失更大一点。很有价值的是,这些功能在C#和D中是已经实现的。由于数组在C#中是不可变的,从tokens中返回一个没有空格的数组将导致重新分配一个数组。可是,D允许指派数组的长度,这个能动态地调整数组的大小这是个很好的例子说明D语言提供更好的效率。

总的来说,考虑到性能和自由度,我们能够说 C++是胜利者,D是很接近的第二名。C拥有最佳的性能,但是只支持一种类型的tokerization。C#表现得很差,java更甚。

总结

我们可以把结果分成3种情况:这些包括语言的特性,包括库的特性,或者两者都相关。这些只与语言相关的特性显示在 f2i, i2f, picalc, picalcr, and sieve scenarios,这些在语言选择上的作用是很小的。c#看起来在总体上是最好的,但是有点让人不能信服的是它在f2i中取得的优异性能并且是因为牺牲了浮点精确度而取得效率。(这一点需要更多大检查,我将在下一篇文章中去做。)

java是明显最差的

当讨论intel编译器的浮点计算的深度优化效果时,它很明显地显示,c和c++能够做的比C#更好。我讨论这些并不是说这些语言必须要更好的效率,而是说他们能比其他的语言提供更好的优化和增强的机会。这个大概是与本主题完全不相干的是,C和C++比别的语言有更多的编译器;更多的竞争,更聪明的人拥有不同的策略和技术。编译器的结果的运行目标是处理器,这将导致戏剧性的效果;我们仅仅在表面上探讨了这个庞大的问题,但是我不久将在以后的文章中回到这个主题。

Strtok 是唯一一个能说是library-only的,在这一点上C#干的并不好。虽然比java快,但是比其他的语言慢2倍或更多。同样令人失望的是VisualC/C++的运行库。在这一点上包括语言和库的效果的是i2str,str2i,strcat和strtswtch,描绘的并不是很清楚。C#明显的在string的拼接这一环节上比java好得多,但是明显地比其他的差。关于尊敬的C,C++,D,只是在一些环节上领先,在另外一些环节上相当地差。

很有趣的是定制的C++的库替换加上深度优化的intel的编译器。

总之,不可能做出定量的结论。从语言的表层来看, C# 和 Java 挺简单的,但低效的库降低了速度。我觉得,当相关的库还不如语言本身的时候, C# 的确实有微弱的优势;但反过来,当库逐渐完善,超过语言本身后, C# 明显就不行了。但还是能看到一点点希望: C# 还是有可能实现预期的目标,因为库比语言更容易实现组装。但是,人们认为由于跟别的语言相比, C# 和 Java 所用的库与语言结合的更加紧密,这些库就可以作为这两种语言效率的关键所在,至少对当前的版本来说是这样的。

正如我所作出的示范(从整数到字符)以及相关提到的(从字符到整数),有人可能会批评者并不符合这篇文章的原意。

更深入的探讨

本文主要探讨了一下 C# 对 C++ 和 Java 可能造成的“威胁”。总的来说,这结果虽然不怎么鼓舞人心,却足以让人吃惊。从效率上看, C# 跟 C 以及 C++ 这些过去的同类产品(假如它们是更高效的)相比只能算是一般水平,至少在基本语言特征的比较上是这样的。从某种程度上来说, Java 也是如此。(我承认我无法证明 C 和 C++ 相对于 C# 的绝对优势。在多年前我的毕业论文分析调查期间,我认识到令人乏味的结果正和令人兴奋的结果一样生动形象,但前者花费更小的经济支出。)

上述结论对于多处理器以及(高性能的文件/网络处理系统)等来说却不一定适用。不过,这确实很好地展现出了这些语言的基本效率以及更复杂的运算所依赖的最基本的库。

就 D 发展状况来看,现在就对它进行这样的归类似乎为时过早,不过它现在的表现确实不错。 D 的效率至少是 C# 的 167% ,其实大多数情况下还不止。有些人指出,目前仅仅还处于字符时代,只需几个人就能完成一个编译器及其附带的库。我猜测, D 有可能发展成一门强大的技术。

就我个人而言,作为一个对 C# 持有偏见的人,偶然还是会对它的性能感到惊讶。我热切地期盼着能将性能的对比分析延伸到更多领域:复杂而高效的内存的应用,多线程,进程通信,特殊文件处理,手写代码开销,这些技术都能让我们对语言进行更深入的研究。能看到 Inter 公司怎样通过定制的 C Library (比如 CRunTiny ;参考 http://cruntiny.org/ )和 STL (比如 STLPort )。

鸣谢

感谢 Walter Bright 为我提供了一份言简意赅的关于 D 的介绍,并及时地指出了一些当时被我忽略的优点,使我能对测试工程进行改进。同时也要感谢 Sun Microsystems 公司的 Gary Pennington ,他为我提供了关于 Java 方面的资料。还有 Scott Patterson ,他对本文的草稿进行了大量的精简,并对全篇文章进行了细致的检查。

0 0

相关博文

我的热门文章

img
取 消
img