C++/C#/F#/Java/JS/Lua/Python/Ruby渲染比试 首先为免误会再次重申本测试有其局限只能测试某一应用、某一实现的结果并不能反映编程语言及其运行时的综合性能亦无意尝试这样做。而实验环境也只限于某机器、某操作系统上并不全面。而且本测试只提供运行时间的结果不考虑、不比较语言/平台间的技术性和非技术性优缺点也没有测试运行期内存。世界上的软件应用林林总总性能需求也完全不同本测试只供参考。由于本人第一次使用Python和Ruby若代码有不当之处敬请告之。当然也非常乐见其他意见。测试内容本文测试程序为一个全局光照渲染器是一个CPU运算密集的控制台应用程序(console application)功能详见前文。在前文刊出后本人进行了一点profiling、优化并把代码重新格式化。本渲染器除了有大量数学运算亦会产生大量临时对象并进行极多的方法调用(非虚函数)。本测试有别于人工合成的测试(synthetic tests例如个别测试运算、字串操作、输入输出等)是一个有实际用途的程序。移植时尽量维持原代码的逻辑主要采用面向对象范式。优化方面不进行人手内联函数(inline function)但优化了一些不必要的重复运算。测试配置硬件: Intel Core i7 9202.67Ghz(4 core, HyperThread), 12GB RAM操作系统: Microsoft Windows 7 64-bit测试名称编译器/解译器编译/运行选项VCVisual C 2008 (32-bit)/Ox /Ob2 /Oi /Ot /GL /FD /MD /GS- /Gy /arch:SSE /fp:fastVC_OpenMPVisual C 2008 (32-bit)/Ox /Ob2 /Oi /Ot /GL /FD /MD /GS- /Gy /arch:SSE /fp:fast /openmpICIntel C Compiler (32-bit)/Ox /Og /Ob2 /Oi /Ot /Qipo /GA /MD /GS- /Gy /arch:SSE2 /fp:fast /Zi /QxHostIC_OpenMPIntel C Compiler (32-bit)/Ox /Og /Ob2 /Oi /Ot /Qipo /GA /MD /GS- /Gy /arch:SSE2 /fp:fast /Zi /QxHost /QopenmpGCCGCC 4.3.4 in Cygwin (32-bit)-O3 -marchnative -ffast-mathGCC_OpenMPGCC 4.3.4 in Cygwin (32-bit)-O3 -marchnative -ffast-math -fopenmpC/CLIVisual C 2008 (32-bit), .Net Framework 3.5/Ox /Ob2 /Oi /Ot /GL /FD /MD /GS- /fp:fast /Zi /clr /TPC/CLI_OpenMPVisual C 2008 (32-bit), .Net Framework 3.5/Ox /Ob2 /Oi /Ot /GL /FD /MD /GS- /fp:fast /Zi /clr /TP /openmpC#Visual C# 2008 (32-bit), .Net Framework 3.5*C#_outrefVisual C# 2008 (32-bit), .Net Framework 3.5F#F# 2.0 (32-bit), .Net Framework 3.5JavaJava SE 1.6.0_17-serverJsChromeChrome 5.0.375.86JsFirefoxFirefox 3.6LuaJITLuaJIT 2.0.0-beta4 (32-bit)LuaLuaJIT (32-bit)-joffPythonPython 3.1.2 (32-bit)*IronPythonIronPython 2.6 for .Net 4*JythonJython 2.5.1RubyRuby 1.9.1p378* 见本文最后的7.更新一节渲染的解像度为256x256每象素作100次采样。结果及分析下表中预设的相对时间以最快的单线程测试(IC)作基准用鼠标按列可改变基准。由于Ruby运行时间太长只每象素作4次采样把时间乘上25。另外因为各测试的渲染时间相差很远所以用了两个棒形图去显示数据分别显示时间少于4000秒和少于60秒的测试(Ruby是4000秒以外不予显示)。C/.Net/Java组别静态语言和动态语言在此测试下的性能不在同一数量级。先比较静态语言。C和.Net的测试结果和上一篇博文相若而C#和F#无显著区别。但是C/CLI虽然同样产生IL于括管的.Net平台上执行其渲染时间却只是C#/F#的55%左右。为什么呢使用ildasm去反汇编C/CLI和C#的可执行文件后可以发现程序的热点函数Sphere.Intersect()在两个版本中C/CLI版本的代码大小(code size)为201字节 C#则为125字节 C/CLI版本在编译时已把函数内所有Vec类的方法调用全部内联而C#版本则使用callvirt调用Vec的方法。估计JIT没有把这函数进行内联做成这个性能差异。另外C/CLI版本使用了值类型并使用指针(代码中为引用)作参数传送。若把C#的版本的Vec方法改写为:123456789//class Vec//{//public static Vec operator (Vec a, Vec b)//}structVec{voidAdd(refVec a,refVec b,outVec c);}那么struct不用GC同时ref/out不用复制其性能会比较高。但是代码会变得很难看:1234567// 原来用运算符重载(operator overloading):a b * c d;// 改用ref/outVec e;Vec.Mul(refb,ref, c,oute);Vec.Add(refe,refd,outa);为了维持让语言正常的使用方法本实验不采用这种API风格(更新:加入了C#_outref测试詳見文末)。然而托管代码(C/CLI)的渲染时间仅为原生非括管代码(IC)的1.91倍个人觉得.Net的JIT已经非常不错。另一方面Java的性能表现非常突出只比C/CLI稍慢一点Java版本的渲染时间为C#/F#的65%左右。以前一直认为C#不少设计会使其性能高于Java例如C#的方法预设为非虚Java则预设为虚又例如C#支持struct作值类型(value type)Java则只有class引用类型(reference type)后者必须使用GC。但是这个测试显示Java VM应该在JIT中做了大量优化估计也应用了内联才能使其性能逼近C/CLI。纯C方面Intel C编译器最快Visual C慢一点点(1.19x)GCC再慢一点点(1.32x)。这结果符合本人预期。 Intel C的OpenMP版本和单线程比较达5.16加速比(speedup)对于4核Hyper Threading来说算是不错的结果。读者若有兴趣也可以自行测试C# 4.0的并行新特性。动态语言组别首先要说一句Google太强了难以想像JsChome的渲染时间仅是IC的16.12倍C#的4.94倍。我有信心用JavaScript继续写图形、物理方面的博文了。以下比较各动态语言的相对时间以JsChrome为基准。 Chrome的V8 JavaScript引擎(1.00x)大幅抛离Firefox的SpiderMonkey引擎(15.09x)。而LuaJIT(3.49x)和Lua(5.16x)则排第二和第三名。 Lua的JIT版本是没有JIT的68%并没有想像中的快但是也比Python(16.48x)快得多。曾听说过Ruby有效能问题没想到问题竟然如此严重(327.31x)其渲染时间差不多是Python的20倍。我认为本实验中不同语言的性能差异并非在于数值运算而是对象生成及函数调用。我使用Python内建的profiling功能: