zoukankan      html  css  js  c++  java
  • C++/C#/F#/Java/JS/Lua/Python/Ruby渲染比试

    来源:http://www.cnblogs.com/miloyip/archive/2010/07/07/languages_brawl_GI.html


    512x512像素,每像素10000个采样,Intel C++ OpenMP版本渲染时间为18分36秒。估计Ruby版本約需351天。

    前篇博文把一个C++全局光照渲染器移植至C#,比较C++和C#之性能。刊出后,园友们不吝指出箇中问题,例如嗷嗷发现C++实现里的随机产生器采用了比较复杂的运行时函数,造成Visual C++和Intel C++的巨大差异;赵姐夫发现C#版本用class竟然比struct快等等。修改这些问题后,园友QiaoJie亦提出,可同时测试C++/CLI,检测其所产生的IL代码,在同样的.Net平台上运行,看看是否比C#优胜。很多网友也提供了宝贵意见,未能尽录,唯有以努力撰文作为答谢。本人陆续移植了C++代码至Java、JavaScript、Lua、Python和Ruby,赵姐夫亦尝试了F#。本文提供测试源代码、测试结果、简单分析、以及个人体会。

    声明

    首先,为免误会,再次重申,本测试有其局限,只能测试某一应用、某一实现的结果,并不能反映编程语言及其运行时的综合性能,亦无意尝试这样做。而实验环境也只限于某机器、某操作系统上,并不全面。而且,本测试只提供运行时间的结果,不考虑、不比较语言/平台间的技术性和非技术性优缺点,也没有测试运行期内存。世界上的软件应用林林总总,性能需求也完全不同,本测试只供参考。

    由于本人第一次使用Python和Ruby,若代码有不当之处,敬请告之。当然也非常乐见其他意见。

    测试内容

    本文测试程序为一个全局光照渲染器,是一个CPU运算密集的控制台应用程序(console application),功能详见前文。在前文刊出后,本人进行了一点profiling、优化,并把代码重新格式化。本渲染器除了有大量数学运算,亦会产生大量临时对象,并进行极多的方法调用(非虚函数)。本测试有别于人工合成的测试(synthetic tests,例如个别测试运算、字串操作、输入输出等),是一个有实际用途的程序。

    移植时尽量维持原代码的逻辑,主要采用面向对象范式。优化方面,不进行人手内联函数(inline function),但优化了一些不必要的重复运算。

    测试配置

    • 硬件: Intel Core i7 920@2.67Ghz(4 core, HyperThread), 12GB RAM
    • 操作系统: Microsoft Windows 7 64-bit
    测试名称 编译器/解译器 编译/运行选项
    VC++ Visual C++ 2008 (32-bit) /Ox /Ob2 /Oi /Ot /GL /FD /MD /GS- /Gy /arch:SSE /fp:fast
    VC++_OpenMP Visual C++ 2008 (32-bit) /Ox /Ob2 /Oi /Ot /GL /FD /MD /GS- /Gy /arch:SSE /fp:fast /openmp
    IC++ Intel C++ Compiler (32-bit) /Ox /Og /Ob2 /Oi /Ot /Qipo /GA /MD /GS- /Gy /arch:SSE2 /fp:fast /Zi /QxHost
    IC++_OpenMP Intel C++ Compiler (32-bit) /Ox /Og /Ob2 /Oi /Ot /Qipo /GA /MD /GS- /Gy /arch:SSE2 /fp:fast /Zi /QxHost /Qopenmp
    GCC GCC 4.3.4 in Cygwin (32-bit) -O3 -march=native -ffast-math
    GCC_OpenMP GCC 4.3.4 in Cygwin (32-bit) -O3 -march=native -ffast-math -fopenmp
    C++/CLI Visual C++ 2008 (32-bit), .Net Framework 3.5 /Ox /Ob2 /Oi /Ot /GL /FD /MD /GS- /fp:fast /Zi /clr /TP
    C++/CLI_OpenMP Visual C++ 2008 (32-bit), .Net Framework 3.5 /Ox /Ob2 /Oi /Ot /GL /FD /MD /GS- /fp:fast /Zi /clr /TP /openmp
    C# Visual C# 2008 (32-bit), .Net Framework 3.5
    *C#_outref Visual C# 2008 (32-bit), .Net Framework 3.5
    F#

    F# 2.0 (32-bit), .Net Framework 3.5

    Java Java SE 1.6.0_17 -server
    JsChrome Chrome 5.0.375.86
    JsFirefox Firefox 3.6
    LuaJIT LuaJIT 2.0.0-beta4 (32-bit)
    Lua LuaJIT (32-bit) -joff
    Python Python 3.1.2 (32-bit)
    *IronPython IronPython 2.6 for .Net 4
    *Jython Jython 2.5.1
    Ruby Ruby 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方法改写为:

    //class Vec
    //{
        //public static Vec operator +(Vec a, Vec b)
    //}
    struct Vec
    {
        void Add(ref Vec a, ref Vec b, out Vec c);
    }
    

    那么,struct不用GC,同时ref/out不用复制,其性能会比较高。但是代码会变得很难看:

    // 原来用运算符重载(operator overloading):
    a = b * c + d;
    // 改用ref/out
    Vec e;
    Vec.Mul(ref b, ref, c, out e);
    Vec.Add(ref e, ref d, out a);
    

    为了维持让语言"正常"的使用方法,本实验不采用这种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功能:

    python -m profile smallpt.py
    

    从结果发现,Vec类共产生约15亿个实例,Vec的方法调用约17.5亿次,intersect()共调用5.7亿次,产生随机数5.7亿个,radiance()调用(即追踪的路径线段)6.5百万次。这些庞大数字,放大了对象生成和函数调用的常数开销(overhead)。

    结语

    也许本博文的意义不大(yet-another-unfair-biased-performance-comparison-among-programming-languages),但对本人而言,此次实验加深了对各种语言性能的了解,或应该是消除了一些误解。简单总括运行性能方面的体验和感想:
    1. C++和VM类静态语言可以大约只差2~4倍,JVM和CLR差异不大。
    2. C++和动态语言之比,则可以是15~5000倍,不同动态语言的差异很大。
    3. 一直以为Lua(JIT)会是最快的通用脚本语言,没想到此测试中败给JavaScript(V8),或许应该多点研究嵌入V8引擎(SWIG能支持就最理想了)。
    4. 以为Python和Ruby的性能相差不远,但测试结果两者大相径庭。暂时不太了解Ruby的特长,或许之后再研究其优点是否能盖过其性能问题。

    最后建议读者,若要为某应用挑选语言,又要顾及性能,那么应该自己做实验去比较。不要盲目相信一些流言或评测(包括本文)。

    附录: JavaScript版本测试

    警告: 建议使用Chrome。Firefox可能会慢得无法响应。


    更新

  • 相关阅读:
    sql测试
    sql时间和日期函数
    递归算法
    冒泡排序
    Ajax
    省市区下拉框三级联动
    Repeater用法
    WIndows form Linq多表联合
    C# 递归算羊
    C# 定义一个学生的结构体,输入学生信息,学号,姓名,身高,按身高排序输出
  • 原文地址:https://www.cnblogs.com/dabaopku/p/2080873.html
Copyright © 2011-2022 走看看