系统级编程语言性能大PK D语言成首选
"C/C++已经统治系统编程很久,除了ObjectiveC之外语言都无法获得很高的关注。有人用多种系统级语言编写了同样的地图生成工具来测试他们的性能,包括D(DMD,LDC,GDC)、Go(GCC-Go,6g)、Haskell(GHC)和Rust。
相比C/C++,这些语言都原生支持了诸如垃圾回收这些高级特性,也因此无一能达到C/C++的运行速度。这其中表现最差的是原生Go语言编译器6g,只有Clang22%的速度,而表现最好的是基于LLVM的D语言编译器LDC,达到了79%。
由于原生就使用了LLVM编译,Rust成为各语言原生编译器里最快的一个,但也只达到了45%。从结果来看,D语言一定是首选。由于D语言许多特性都依赖垃圾回收,如果需要关闭垃圾回收而又要保持良好的使用体验,则推荐Rust。" (摘自:Solidot)
Go,Rust,Haskell和D四语言做关卡基准测试
作者在为一款游戏制作随机关卡生成器,尽管这个游戏是用C++编写,且是模块化,但是类似的关卡生成器却可以用更高级的语言来编写。因为C++并不是最好玩和最丰富的语言,所以作者打算选择其他语言替代。
“如使用特别依赖迭代和条件句的简单Roguelike关卡基准,然后粗略模仿他们的真正生成器。”点击此处获取代码。作者认为Haskell语言的是最易读的版本:
roomHitRoom Room {rPos=(x,y), rw=w, rh=h} Room {rPos=(x2, y2), rw=w2, rh=h2} | (x2 + w2 +1 ) < x || x2 > (x+w+1 ) = False | (y2 + h2 +1 ) < y || y2 > (y+h+1 ) = False | otherwise = True
检查新生成的room与原有的room是否有冲突,如果有冲突就放弃(这是强攻关卡技巧;我们真正的引擎要更为复杂,但仍然遵循同样的原则)。大多数剩余的时间花在了随机数字生成上,所以给基准加点料,使其像常速那样具备单独语言的随机数字生成器(例如,这些基准与作者的目标相关,而与你的无关。)。
PS:所有从Haskell分离出的部署现在都使用XorShift PRNG方法,比较起来更合理。
事实上,只有LDC D的使用XorShift,因为DMD的用XorShift运行起来比用C rand()要慢一些。一个部署XorShift PRND的Haskell版本可能是最受欢迎的。
结果如下 (C 语言版本用于对比)。
编译器 LDC | Clang | GCC | Rust | GDC | DMD | GCCGo | GHC | 6g 时间: 0.412| 0.280 | 0.390 | 0.620 | 0.680 | 0.770 | 0.850 | 1.05 | 0.544 最快(%): 68% | 100% | 72% | 45% | 41% | 37% | 33% | 27% | 51%
LDC,Rust和Clang所使用的LLVM版本是3.2.GCC,GCCGo使用的是GCC 4.7.3版本,GDC使用的是 GCC 4.6.4版本。Rust 是0.8-pre (9da42dc 2013-07-17 04:16:42 -0700) 版本,GHC是 7.6.2版本,DMD是2.036版本,6g(Go)是1.1.1版本。它们运行的都是03,-opt-level=3可用于Rust,-release可用于DMD,-funbox-strict-fields可用于Haskell。**D现在运行的时候同样有-inline和-noboundscheck,所以其速度从1.36s增加到了1.59s。
D是目前测试过的最快的非C语言。在这里不得不表扬LDC编译器的设计师们。作者非常期待着一年以内再次运行这些基准,看看Clang,LLVM D和LLVM Rust运行得怎样,并对它们做出比较。
Rust,虽然最初比较慢,但是通过XorShift PRNG,其速度已经得到大幅提升。但是为了使Rust的句法适应语境,所以Rust的灵活性要受一点点影响;要通过堆形分配的向量,不过需要引用myFunc(&mut myVector),接收到它的函数要在其类型签名有myAlias:&mut~[myVector] ,和fn myFunc(myAlias:&mut ~[myVector]) {..} 一样。与C比较则是
void myFunc(struct MyType myAlias[arrayLength]) { .. }
Rust版本看上去有点拜占庭的味道。还没有用过Rust的人可以看看,这里给出7种指示符: @ (收集垃圾的堆形分配), ~ (独特的堆形分配), & (借给堆形/堆栈分配的指示符), * (原始的C指示符, 仅限于不安全代码), 以及前三种符号的变体(事实上,不确定是否真的有&指示符,所以可能总共只有6个)。注意,具有可变值的指示符和没有可变值的指示符是截然不同的。
作者还用堆栈分配型向量对Rust版本做了基准测试,但是速度方面的差异不明显。不过发现Rust中的堆栈分配型向量目前有点冗长,因为它不允许未初始化的值,所以不得不创建一个对象实例,作者想要一个所有值都被设置为零的数列,然后用上述创新来填充向量。希望,不久的将来,Rust也能学习Go,将所有数值自动设置为零,或者至少提供这样一个可选项。目前,它看上去就像这样:
(显然,这种想法已经成为可能(https://news.ycombinator.com/item?id=6094819)。希望下次升级的时候,Rust教程里能将其记录下来。)
et emptyr = Room {X:0,Y:0,W:0,H:0,N:0}; let emptyt = Tile{X:0,Y:0,T:0}; let emptyl = Lev{TS : [emptyt,..2500] , RS : [emptyr,..100] }; let mut ls : [Lev, ..100] = [emptyl,..100];
这里面有很多不必要的代码,如果是使用堆栈分配型数列较多的大型项目,这就非常累赘。
Go给人的印象很深刻,虽然不如D,但是作为一种相对比较新的语言,已经非常不错了。默认的PRNG在速度方面有缺陷,所以可改用XorShift。分号的自由使用是一种很好的体验,就作者个人经验而言,它很适合写命令式代码。
尽管作者花了很长时间做优化,但是他仍然喜欢Haskell的性能,特别是它必须得携带一个包含一千万整数的向量。递归式编写是一种很棒的改变,而Haskell版本是所测语言中最简明的。
PS:升级Haskell,用MWC生成器取代Mersenne,速度可达1.05s。
小结两点:没有运行慢的语言,只有优化不到位的汇编;如果不需要加密级别的随机性,那么XorShift是速度性能最卓越的PRNG运算法则。
——原文参考:Benchmarking level generation: Go, Rust, Haskell and D (and now Scala).
接着Solidot上面所说的,C/C++已经统治系统编程很久,除了ObjectiveC之外语言都无法获得很高的关注!那么为什么说C语言是系统级编程的首选?
下面我们摘自知乎上张泊宁回答的这个问题很有很有见解,引用至此分享给大家:
第一, C 语言编译出来的代码执行效率高。Java 是编译出来的是字节码而不是计算机可直接读的指令,执行时候还要再翻译一遍。虽说这个翻译过程还是很快的,但对于性能要求比较高的系统级软件仍然是效率优先,不能使用类似 Java, C# 编译出的字节码程序。
第二,C语言的指针功能非常强大,一些像树、表这样的数据结构离开指针可谓寸步难行。而且指针操作非常高效。 但指针操作对于程序员来说很容易在使用中出错,因此 Java 不支持指针。再加上上面说的效率问题,所以没有指针的 Java 不能用来编写系统级应用。