Hello World
Spiga

为什么我支持托管运行时(虚拟机)

2010-07-01 11:37 by 老赵, 6408 visits

最近博客园上在炒关于C#性能的问题,其实应该说是.NET性能的问题,其中某位仁兄提出,他希望C#能够直接编译为原生代码,而不是在CLR这样一个托管运行时上执行,因为虚拟机啊,JIT什么的性能差。后来发到TL上以后,也有朋友认为,“基于虚拟机的语言都是大公司为了利益在推动,说白了就是政治”,因此“对C#提高性能的建议感到可笑,因为它本来就不是用来开发高性能程序的”,再有,“C、C++已经明确不和这些后进争所谓的‘容易开发’的头衔”,那么其他语言为什么要和C++它们比较性能呢?我是托管运行时,或者虚拟机的忠实拥护者,这里谈一下我在这方面的看法。

我并不反对编译为原生代码的语言,尤其是C语言,它的意义在于提供了一种对硬件完全控制的手段,对硬件提供了一种最直接的抽象,几乎可以映射到最终流程控制方式,因此无可替代。C++作为C语言的超集,提供了更丰富的抽象能力(如面向对象和模版化),只是语言本身过于复杂,超过了以我的智商可以承受的范围,因此我学了几次都没怎么学会,现在更是忘得差不多了。不过我认为,越来越多的语言会构建在托管平台上,而不是直接编译成原生代码。因为一个统一的托管运行时会带来很多好处。

首先,统一的运行时提供了跨平台的能力,Java便是一例典型。.NET上有mono,使用也很广泛,也有不少如Unity3DGnome DO等成功案例。Novell,包括其他一些公司也在销售基于mono的商业产品(如MonoTouchInfragistics的ASP.NET Controls组件),我本身也在两年多前就在生产环境上使用了mono,您现在看到的这个博客也是基于Ubuntu Server、mono 2.6 、Apache以及微软开源的ASP.NET MVC 2构建的。虽说从某些层面(如API兼容性)上说,mono的跨平台性远不如Java平台,但它也是一个比较成熟的执行环境,并具备相当程度的跨平台能力——尤其是在mono上开发,MS .NET上运行的时候(虽然我不建议这么做)。如今支持mono的产品、类库数不胜数,我时常调侃道,“如果您的产品不支持mono,还真不好意思和人打招呼”。只可惜,不少人都用一些“不是亲娘生的”类似的调调来否定mono,在我看来没有经过调查研究的看法只能属于“臆断”,而且更是一种FUD了。

即便退一步来说,我们不“跨操作系统”吧。有人说,.NET就支持Windows么,何必搞个虚拟机,还JIT那么麻烦。但事实上,“跨平台”并非指的是简单的“跨操作系统”,而是“跨执行环境”,如Silverlight。事实上,跨计算机体系结构本身也是种跨平台(当然,操作系统其实已经进行了一定的统一抽象了)。因此,虚拟机的目的,是为上层执行体抽象出了统一的运行环境——这其实还是跨平台,这平台不仅仅是指操作系统,整体运行环境之间的差异也是运行时所“抽象”的一部分。比如在并发环境中,不同CPU架构的流水线上的乱序方式不一样,同样的代码执行的效果就可能不同。最典型的例子,便是JVM之前的内存一致性模型控制的比较宽松,导致经典的double check模式在某些CPU上是会失败的。现在Java标准也变得严格了,和.NET CLR一样避免了Store Reordering。这意味着在某些CPU上,会在特定的地方加上Memory Barrier保证执行效果的一致性。在我看来这是更好的可移植性。C++或是C语言等实现“可移植性”的方式,往往是通过为不同环境提供不同的编译器,生成不同的结果,而且会使用“宏”等方式,在代码里写出有平台针对性的代码。

有了统一的运行时,也可以让多语言互操作更为容易,如果没有JVM或CLR,就很难像现在这么轻松地在Scala/Java/Jython/JRuby,或是C#/F#/IronPython/IronRuby,甚至是未来的语言之间进行直接地互操作,更难做到“无缝集成”了。在合适的场景下选用合适的语言,是提高生产力的重要手段。如果没有JVM平台,就很难使用Scala来代替Java这样的劣质语言,而现在Scala便能够保证充分利用Java平台上类库等积淀。

有了“多语言”,那么便会引出虚拟机的另一个作用:让语言实现者和虚拟机实现者的工作可以分离开来,各自优化。虚拟机的实现者可以尽力优化虚拟机的各种表现,为虚拟机加入各种优化措施,而无需让虚拟机的上层语言分别调整。例如JVM性能提高之后,Scala、Java、Jython、JRuby等语言的性能都可以提高,否则各种语言分别优化的代价太高了(当然某些情况还是需要特殊对待的)。要说这方面的实例,在.NET平台上有个开源的组件DLR(动态语言运行时),是在CLR上提供编写动态语言的统一辅助类库。以前它有个缺点,便是启动速度比较慢,因为动态代码的JIT耗时较长,而动态语言的很多场景是“即用即抛”的。后来,DLR增加了一个优化策略,便是一开始直接对抽象语法树直接解释执行,而执行多次之后才使用后台线程将代码JIT成原生代码。有了如此改进,基于DLR的动态语言,如IronRuby和IronPython的启动速度都提高了。

虚拟机/运行时本身可以做的优化也很多,如果真觉得性能不够,那么完全可以在运行前在本地编译成native code,这和直接从源代码变成native code从结果上看没什么区别。但是现实是,很少有人去这么做,因为这么做往往只是节省了JIT的开销,对性能提高效果不大。在不同环境中,此外还有各种优化,比如使用解释执行,而不是JIT以次节省内存消耗,或是在运行时回收JIT的代码(印象中在.NET Compact Framework里有这样的策略,求详情),或是在运行时根据代码逻辑进行二次编译。下面会谈一个例子。另一种典型的优化,一直在研究却还没有真正实现的,虚拟机便是“自动并行”。关于这点,Anders在上次的演讲中多次提到过,要实现这点还需要有各种支持,如声明式编程,提供“无副作用”标记,甚至在语言级别的支持等等。

之前那位朋友提到,C/C++已经明确不与后进语言比生产力了,后进语言也没办法和它们比较性能。对这个观点我持保留意见,因为基于虚拟机的做法,其优化空间还有很多,理论上也可以做到更为彻底,在许多情况下性能完全有超越静态编译语言的可能。这是许多人的看法,而事实也是如此,在某些场景下Java的性能也已经超过了C++。如回到上面的二次编译优化,对于性能优化也大有好处。举一个简单的例子,面向对象语言会出现很多虚方法调用(尤其是在符合一些关于设计的最佳实践时,如“基于抽象编程”),调用需方法需要查方法表找方法入口,最普通的做法就是必须每次根据对象的实际类型查找方法表,找到地址,然后调用。伪代码如下:

根据实际类型找到函数入口
调用

但是在实际执行过程中,可能有某个特定类型出现的次数特别多,甚至完全只会出现一种具体类型的实例(抽象只是为了扩展性或是单元测试等等),但因为虚方法的调用语义,我们就无法对这次调用进行内联优化等等。不过在托管的运行时中,如果发现某个特性类型出现次数特别多,则还是可能将那个类型的方法内联进来,然后只在“不是那个实例”的情况下才去查找方法入口。伪代码就是:

if (对象为不是类型A) then
    根据实际类型找到函数入口
    调用
else
    执行内联后的类型A的代码
end

类似这种的优化不是空中楼阁,它们在HotSpot(即SunOracle的JVM)是确切实现的。Hotspot的JIT,尤其是加上-server开关时,它的优化会变得十分激进,而CLR的JIT与它相比就像是静态编译器了。C++在科学运算中性能高,往往是靠大量的inline和精细资源控制,但是这说实话都还是静态的“手动优化”,如果比实际工程的真实运行情况,比如上面说的虚方法跳转,还真不见得C++可以超过托管代码。虚拟机做的则是动态优化,对于长期执行的代码甚至可能执行的越来越高效,还能够针对环境改变作出调整。总而言之,托管代码可以运用的优化策略实在太多了。随着技术发展,托管代码的速度可以进一步提高,而静态编译代码的空间就小得多了。

对此Milo Yip老大做过一些补充,他指出这方面不同的C++ compiler有不同優化方法,例如VC2008開始有的Profile-Guided Optimizations

Virtual Call Speculation - If a virtual call, or other call through a function pointer, frequently targets a certain function, a profile-guided optimization can insert a conditionally-executed direct call to the frequently-targeted function, and the direct call can be inlined.

我简单地思考了一下,从理论上说,各种优化策略都可以通过静态编译的方式写入原生代码,因此JIT能做的事情,native code理论上都能做。不过,这就意味着要在每个程序中带上“负责优化”的代码,而不光是程序的“功能代码”。用虚拟机,就意味着所有的程序都共享了虚拟机的这么一套优化机制。如果虚拟机的优化手段改进了,那么所有基于虚拟机的程序都能获利,而静态编译的程序,往往只能靠“重新编译”来得到优化了。

此外,对于面向对象语言来说,虚函数是很常见的,虚函数之间各种组合调用,分支、路径也特别多,到处都可能使用虚函数。我“猜想”,像VC这样的编译器,为每个虚函数调用之处都提供了基于profiling的内联优化是不太现实的。而且,为每个虚函数调用的地方都提供“内联”和“不内联”两种版本也不太可能。而基于虚拟机的话,它就可以动态的进行各种优化,每段JIT后的代码都可以回收再动态生成,这种优化是静态编译难以比拟的。

诚然,就目前来说,就性能方面,许多情况下还是静态编译配合手动细节优化可能获得更好的效率,但利用托管运行时获得的好处也是非常可观的,而且我也一直没有遇到过这方面的效率问题。同时,我认为托管运行时的愿景也十分现实可靠,因此就我看来,托管代码是未来趋势,除了越来越小的特定领域,越来越多的程序和语言会构建于托管平台上。事实上,正因为上面这些好处(例如独立优化),越来越多的语言也开始加入虚拟机这样的策略了。例如RubiniusPyPy等新的Ruby和Python语言实现,其实都是有个虚拟机这样的机制存在。

最后我再多说一句:我并不是说追求性能的做法不对,我也从来不会说追求性能本身没有意义。但是我不同意说“考察托管语言性能”没有意义,更是完全不同意说托管代码虚拟机“本身意义就不大”,“完全是大公司在推动”,或是“政治因素”云云。技术就是技术,这些技术上投入了无数天才的精力和创意,这不是什么“政治”之类说法就能带过的。

Creative Commons License

本文基于署名 2.5 中国大陆许可协议发布,欢迎转载,演绎或用于商业目的,但是必须保留本文的署名赵劼(包含链接),具体操作方式可参考此处。如您有任何疑问或者授权方面的协商,请给我留言

Add your comment

57 条回复

  1. infinte
    60.166.103.*
    链接

    infinte 2010-07-01 11:41:03

    沙发?

    其实我同样很支持托管运行,指针我真的受不了……

    当年搞OI的时候就被指针弄得晕头转向

  2. 老赵
    admin
    链接

    老赵 2010-07-01 11:53:09

    @infinte

    你用C搞OI?我记得当时用Pascal搞OI的时候基本不需要指针的吧。

  3. sunli1223
    220.181.24.*
    链接

    sunli1223 2010-07-01 11:53:39

    具体环境具体分析。 虚拟机其实节约了很多开发成本,很多时候对于损失的性能是完全值得的,况且很多事我们的系统都是分布式的结合使用的。比如你用的数据库,很可能就是C语言写的。而对于系统的瓶颈的地方,你可能也会考虑使用C来实现,但是大多时候,我们还是把代码托管在虚拟平台。

  4. infinte
    60.166.103.*
    链接

    infinte 2010-07-01 11:55:02

    @老赵

    我用pascal的,不过二叉树啊,链表啊之类的肯定用指针……记得做一个平衡树(SBT)的时候,被指针弄得头都大了

  5. 老赵
    admin
    链接

    老赵 2010-07-01 11:56:54

    @infinte

    噢,好像是啊,年代太久远了……唉,我怎么就堕落了呢?当年一起搞OI的好像混的都比我好。

  6. weiwei
    114.176.248.*
    链接

    weiwei 2010-07-01 12:28:49

    讲得好多啊.作者的积累真是太厚了.不过,这些东西能说明什么问题吗.该用的人还在用,不该用的人还是不用.

  7. yuanyi_wang
    218.25.161.*
    链接

    yuanyi_wang 2010-07-01 12:37:08

    对于复杂的应用程序,托管代码能更容易的取得良好的性能,毕竟在这个硬件很便宜的时代,托管代码更容易的造出一个砸机器就取得高性能的程序。我觉得,这个对于很多公司更为重要。当然,关键的计算密集型算法和硬件控制功能还是得依赖与C甚至是汇编。 总结起来就是:具体问题具体分析,该用啥就用啥。

  8. gebenxiang
    220.248.45.*
    链接

    gebenxiang 2010-07-01 12:40:19

    mips公司为android dalvik jvm做了个mips的实现, 就可以在mips android上直接跑arm android上以编译的程序。这多美妙?

    vm有很多奇妙的应用。比如有人通过虚拟机把Doom游戏直接跑在adobe flash的VM上

  9. shitmono
    134.159.152.*
    链接

    shitmono 2010-07-01 13:12:02

    博主可以老实说说盛大有什么产品用.net开发的吗?如果没有,你能说服他们吗?如果说服不了,你还会在里面呆下去吗?

  10. 老赵
    admin
    链接

    老赵 2010-07-01 13:24:05

    @shitmomo

    如果是已经发布的产品,我知道起点是用.NET开发的,现有的产品是估计不会迁移到.NET上。我这边开发新产品时无所谓用什么技术,就看谁提出的,谁想用什么。我的东西是.NET和*nix都有,还有一些产品的后端用的是.NET,具体是什么还不能多说。

    至于如果有什么情况逼的我.NET哪儿都不能用,那么我就会看有没有我感兴趣的东西。如果没有的话那么就可能不呆的,不过我又不是非.NET不做的人,我对许多东西都感兴趣。

  11. 老赵
    admin
    链接

    老赵 2010-07-01 13:26:50

    @weiwei: 讲得好多啊.作者的积累真是太厚了.不过,这些东西能说明什么问题吗.该用的人还在用,不该用的人还是不用.

    对于听什么都无所谓,或是坚持自己什么都听不进去的人来说,自然没有多少作用。不过我相信总有愿意思考讨论的人,我写文章是表达自己看法,不是强求别人接受,本来这些就是各方发表自己看法然后再总结的。

  12. 老赵
    admin
    链接

    老赵 2010-07-01 13:36:09

    @yuanyi_wang: 对于复杂的应用程序,托管代码能更容易的取得良好的性能,毕竟在这个硬件很便宜的时代,托管代码更容易的造出一个砸机器就取得高性能的程序。

    我的看法是不仅是“靠砸机器取得高性能”的场景。

    其实现在原生代码性能更高,基本还是高在“细节”上,如果程序员可以得到足够的信息,那么自然可以进行手动优化得到更好的效率。不过,如果出现某些优化是复杂度很高,几乎人力不可控的情况,那么托管平台的优势就出来了。其实普通人写汇编还真不一定有C语言性能高,也是同样的道理。比按照规则进行优化,机器比程序员要强大无数倍。

    而且,对于纯计算密集型的东西来说,因为规则就这么点,人力可以涉入优化。但是如果是复杂的程序,比如我文章里写的虚方法的内联调用,甚至要根据长期结果进行自适应的话,就几乎一定是托管代码了。

  13. 链接

    秋实 2010-07-01 13:48:20

    Reflection等一批概念也是建立在Runtime基础之上的吧,提高生产力是一定的,而改变编程方式应该属于质变了

  14. 链接

    mfjt55 2010-07-01 14:04:32

    对此Milo Yip老大做过一些补充,一些补充上的链接好像打不开。

  15. DJ
    183.3.171.*
    链接

    DJ 2010-07-01 15:15:25

    Profile-Guided Optimizations那个链接写错了吧.

  16. 链接

    twty1988 2010-07-01 15:25:40

    我觉得托管运行时(虚拟机)主要还是解决了硬件的细节问题,让程序员把精力集中的业务上。

  17. 老赵
    admin
    链接

    老赵 2010-07-01 16:14:01

    @DJ

    谢谢,改好了。

  18. avlee
    119.62.30.*
    链接

    avlee 2010-07-01 16:15:06

    某些情况下性能优于C++应该是实际情况,关键是这种情况就不要使用C++了,现在都混合编程,干嘛要一棵树抱着死呢?其实有些人可能想表达的初衷不要是一味的追求纯C#纯java。

  19. Paul Shen
    117.84.247.*
    链接

    Paul Shen 2010-07-01 16:38:22

    千万不要忽视理论。 有了理论才能解决实际问题。

  20. Paul Shen
    117.84.247.*
    链接

    Paul Shen 2010-07-01 16:42:31

    @老赵

    请问能达到C+操作系统的水平吗?

  21. Paul Shen
    117.84.247.*
    链接

    Paul Shen 2010-07-01 18:20:44

    我还是用C吧,阿门

  22. 链接

    Ivony 2010-07-01 18:28:49

    其实了几天了,真的没看到所谓的反对firelong阵营这边有谁说C#快了,顶多是证明“不慢”,C#在大多数应用下性能不如native从来没有人去否认过。“C#很快”这个大帽子本来就是强加在所谓的“老赵们”头上的,然后再拉出去批斗。

    既然说了是实际应用中了,那问题就复杂了,就不可能简单的说,因为C#所以慢,或者,因为JIT所以慢,因为元数据所以慢。这些都是毫无根据的臆断,说白了就是梦话。然后再偷换概念,C#慢是“众所周知”的,所以C#体系架构就有问题了,这也是“众所周知”的,微软的路就走错了,这也是“众所周知”的。这不是扯淡么?

    如果C#根本(体系架构上)就是有问题的,是无论如何也搞不好的,那怎么去说明在特定情况下C#就性能差不多,甚至更好呢?所以问题不会出在根本上。也不可能如firelong那样把C#断手废脚就能快了。

    顺便回答几个问题:

    1. 为什么没看到几个很慢的C++程序。
      答:快和慢是相对而言的,在一个最快就是C++的编译的native code的软件环境下讨论为什么没有比C++快的程序是毫无意义的。因为Windows API不是汇编的,现在也没有人用汇编去写Windows Application。我们假设现在Windows API撤掉,全部用.NET提供,我看在Windows下C++也未必快的起来。就像DOS时代的API是中断,C++未必比汇编性能好。

    2. 为什么微软不把以前的软件都用.NET重写,这样就可以用.NET来开发新功能了。
      答:用.NET重写不是用.NET开发新功能的必要条件。VS、SQL、Office、Windows都不是.NET写的,但这不妨碍它们可以用.NET扩展功能,至于VS,现在已经用WPF做界面了。这么多年了磁盘的引导扇区和BIOS也不是C++写的,为什么不用C++去重写一下?

    3. mono或.NET对Java优势何在?
      答:Mono只是提供了多一种可能。.NET本来的定位就是替代Windows API,也没打算在Unix上与Java一较高下。难道微软把Java从Windows平台上赶出去了不是一种胜利么,至少我见到的跑在Windows平台上的.NET应用比Java的多。再说了,如果Java占领了其他所有的桌面平台,但是却被从全球80%以上的桌面(Windows)中赶了出去(假设)。那到底是谁赢谁输?对于非桌面,.NET 里面与Windows平台捆绑不可分割的部分,除了UI又还有多少呢?

    结语:废话这么多,不是为了吵架,只是想试试老赵的这个博客的有序列表的样子。

  23. 老赵
    admin
    链接

    老赵 2010-07-01 18:34:37

    @Ivony: 废话这么多,不是为了吵架,只是想试试老赵的这个博客的有序列表的样子。

    唉,我不会CSS,样式调不好看。

  24. 链接

    miloyip 2010-07-01 22:08:51

    本來一早都想在TL回的,但最近TL比較亂,就在此討論吧。

    C++或是C语言等实现“可移植性”的方式,往往是通过为不同环境提供不同的编译器,生成不同的结果,而且会使用“宏”等方式,在代码里写出有平台针对性的代码。

    其實這只是平台抽象的地點不同。我也可以改寫為:

    C#或是F#語言等實現「可移植性」的方式,往往是通過為不同環境提供不同的VM,生成不同的JIT後原生代碼。

    採用C/C++標準的代碼,本身是跨平台的(雖然還要注意CPU架構,例如sizeof(int)和byte order)。一般的問題是,C/C++的標準庫提供的東西太跨平台,缺乏很多常用的功能,例如GUI。但若採用如Qt或wxWidget等庫,還是可以簡單地寫跨平台的GUI,在應用程序的代碼中不需要用宏作跨平台之用。OpenGL也是一個「理論上」跨平台的C API。但通常VM提供的庫比較標準化,而C/C++則是多元化。

    这意味着在某些CPU上,会在特定的地方加上Memory Barrier保证执行效果的一致性。在我看来这是更好的可移植性。 C/C++標準中,並沒有多綫程。所以C/C++在這方面的移植性基本上可以忽略不談。但在性能上,由於C/C++必須使用OS提供的功能,所以可以選擇該OS效能較好的同步機制。但要用C/C++寫出正確且高效的多綫程程序,一般會比C#難(OpenMP除外)。

    看到本文指向之前一篇文章(在某些场景下Java的性能也已经超过了C++),有關pointer aliasing的C++和Java性能問題。C++編譯器提供一些聲明方式來解決,例如VC有_declspec(restrict)、_declspec(noalias)和__restrict。

    不過這是都是由於C/C++接近CPU,有pointer機制才會產生這個問題。在其他語言中,可以保證兩個數組不會指向同一個地址。C/C++的難點也在於此,必須更了解低層,才能寫出高性能的實現。而使用VM,則有時候控制權不在程序員手中。這都是雙面刃。

    而且,为每个虚函数调用的地方都提供“内联”和“不内联”两种版本也不太可能。

    事實上,就算不使用profile guided optimization,C++對於一個虛函數,是分開虛調用和非虛調用的。典型的例子是在virtual destructor中,調用父類的destructor,父類的destructor是可以inline進子類的destructor中。常說C++的EXE大,有很大部份原因就是因為inline(及template)所致的。所以我認為,說C++不可能提供兩種版本,証證是不太充份。

    而基于虚拟机的话,它就可以动态的进行各种优化,每段JIT后的代码都可以回收再动态生成,这种优化是静态编译难以比拟的。

    這個概念其實像profile guided optimization,不過是動態進行。但兩者皆有共同缺點。它們都是估計將來的輸入數據特性相近,所以才可以透過統計方式提高期望的(expected)效能。但無論靜態或動態,都難以估計將來的輸入數據是否和profile的接近。動態的話,有可能可以進行適應性調整,但這種算法也不一定能帶來效能提升,反而會有profiling和JIT的開銷。

    但總括而言,我也認同趙姐夫的說法。我認為VM是一個技術歷史上的突破,對多個因素(包括性能、安全性、開發速度、可維護性等等等等)有適當的平衡。C/C++是適合某些需要極端性能並和硬件打交道的應用(OS、web/database server、game)、或性能十分有限的系統(embedded、mobile)。一舨的應用程序,客戶和硬件的需求變化較多,使用VM很多時候較為合適。反而是最近測試過一些動態語言,雖然有更大的動態功能,但是要換取幾個性能數量級。這更讓我懷疑純解譯型的語言實現,適用時機是否足夠。

  25. live41
    58.249.27.*
    链接

    live41 2010-07-01 22:28:13

    为什么微软不直接把代码生成到标准的PE格式?

    一定要楞个IL来执行,莫非是为了好让最后通过IL在各语言之间转换?

  26. clotho
    58.249.27.*
    链接

    clotho 2010-07-02 01:07:21

    现在的程序,实际上没有几个是专门为跨平台而设计..

    既要在win运行,又要在linux运行的,这样的构想除非是很多地方用到,否则争论虚拟机是浪费时间..

    像C语言要用到线程,完成端口(linux下用epoll)等一些系统特有的特性,也不是跨平台的,最后还是要移植..

    先不讨论.net和jre慢不慢,我只是觉得弄个虚拟机说目的是为了跨平台有点废..

    因为跨平台的设计少之又少,特别是在中国..

  27. clotho
    58.249.27.*
    链接

    clotho 2010-07-02 01:15:43

    上面的话逻辑有问题.. 我要表达的应该"跨平台的.net程序很少",

    我只是见过在win下写java代码,然后拿去linux重新编译再运行..

    很少见到人在win下写C#代码,然后拿去linux下用mono重新编译运行..

    如果有人这么做,是我孤陋寡闻..

  28. 老赵
    admin
    链接

    老赵 2010-07-02 10:14:54

    @clotho: 我只是见过在win下写java代码,然后拿去linux重新编译再运行..很少见到人在win下写C#代码,然后拿去linux下用mono重新编译运行..

    国内mono用的很少,自然不会在win下写C#,到Linux下去编译。国外的话,关注一下一些开发者大会,或是CodeBetter这样的社区,就会发现很多人用Mac写C#代码,然后运行部署在Win下。这是因为:

    1. .NET程序毕竟大部分还是部署在Win下的。
    2. 写mono部署到.net上基本不会有问题,mono实现的api是.net的子集,当然是足够用的子集。
  29. 链接

    Ivony 2010-07-02 13:27:35

    Java那种NC的架构我从第一天就不喜欢微软跟他统一。。。。

    直到今天,我都不满意微软要遵循W3C标准,看看那个NC的组织有异于地球人的想法和树懒般的效率吧。。。。。如果都遵循微软的标准,世界清净很多。这算不算微软被陷害的一个实例?

    我想程序员还是应该选择对的,选择自己喜欢的,不要过多的去讨论什么阴谋、商业、背景。BC的确做的比MC好,这一点我不否认,所以大家都用BC,很少用MC吧,至于你说的OLE什么的,个人感觉还是以偏概全。如果现在VS还是以前MC那个破样子,支持.NET我也不用。

  30. 链接

    slicelee 2010-07-02 14:53:50

    Android的成功说明了什么?谷哥弄的那个可不是为了跨平台,最多是为了跨硬件架构,ARM,X86,MIPS最好都能用上。 记得WP7最先的展示还不太让人满意,不过最近看到的真机视频操作的响应和流畅度都和IP伯仲之间了,至少在硬件保证的情况下性能不是问题了。 这两个都算很典型的例子,而且手机还是需要非常注重性能的,呵呵。

    最后的问题就是C#理论来理论去基础好像都没问题,但Winform很多都比较悲剧,WPF的资源消耗更是吓人,这也确实是事实,除了微软自己的Expression Studio等做得优秀点。

    但性能差的根源如何避免如何优化,却没有能够真正的有理有据的来说明,winform进军桌面除了快速开发和C++,dephi比起来没有自己特有的优势,而WPF在呈现方面的优势是很诱人的,要进军桌面,就现在大家用的电脑来说,探讨性能的优化还是很重要的。

    而且现在的Net程序员对算法对性能对计算机硬件架构体系的认识都很糟糕,我作为一个糟糕的Net程序员,看到自己过去的糟糕的代码,甚至连算法都谈不上调用现成的东西拐了几个弯来达到自己的需求比比皆是,博客看多了更觉得现在的代码一如既往的糟糕,看得越多越觉得糟糕。 不过可喜的是糟糕的代码一样满足了客户的需求,这就是Net吧,但WPF这些要进军桌面就不得不有所突破了。

  31. 四有青年
    210.13.101.*
    链接

    四有青年 2010-07-02 15:55:06

    很久没看到这样营养丰富的文章了,分析的很细致

  32. anoter_guest
    148.87.19.*
    链接

    anoter_guest 2010-07-03 16:47:44

    解释“跨平台”那一段,为什么不从ABI和ISA的角度出发来说呢?那样该会有多么的清晰和明白

  33. 链接

    xiaotie 2010-07-04 19:02:04

    @mcpssx

    C#,Java,ActionScript3性能是一个级别的,ActionScript略慢,慢约20%左右。这个我验证过多次。ActionScript3和ActionScript2的性能据说不是一个级别的,前者据说快10倍以上。不过我怀疑这个结果,但没验证过(没验证的意义)。如果全部用ActionScript的动态特性写出的程序,当然就是动态语言性能那种档次了,如果用它的静态特性写出的程序性能就和C#,Java近似一样了。

  34. 链接

    xiaotie 2010-07-04 19:13:45

    @mcpssx

    特定环境的测试是不能说明问题的,我已经说过了,在这种加减乘除的测试中,java的速度绝对比actionscript快一个数量级,但是flash和swing的给用户的感受也是很明显的。这时候你应该先从客户感受定性入手,而不是反过来用加减乘除测试来证明swing比flash快

    不要道听途说。actionscript 1.0, 2.0 和 3.0 之间的差异比 从c到c++到c#之间的差异都大。完全就不是一种语言。单说actionscript不说版本就类似于说“C、C++或C#”?加减乘除的测试,actionscript 3 和 c# 是一个数量级的,甚至浮点计算,actionscript 3 比 c#还要快一点点。

  35. mcpssx
    113.57.9.*
    链接

    mcpssx 2010-07-04 19:44:11

    @xiaotie

    你说的也可能我记错了, 不过我说的用户比较flash和swing程序感觉的时候, 显然还没有有静态类型声明的actionscript3。

    早在as3出来之前,flash给用户的感觉就比applet好。

  36. 链接

    xiaotie 2010-07-04 20:34:31

    我错了。M个月前测了一次,C#和as3差不多。N个月前测一次,也差不多。刚才又测了一次,换上了flash 10.1和sl4,结果大掉眼镜,sl成绩提高了1倍fl成绩下降了将近1倍。

  37. RednaxelaFX
    121.0.29.*
    链接

    RednaxelaFX 2010-07-06 21:16:59

    好久没来乱入一下了…

    或是在运行时回收JIT的代码(印象中在.NET Compact Framework里有这样的策略,求详情)

    .NET CF是有可配置的固定大小的code cache来保存JIT后的代码;满了之后就做code pitch(抛弃代码),把整个code cache清空然后重新JIT。SSCLI同样支持code pitching。

    這個概念其實像profile guided optimization,不過是動態進行。但兩者皆有共同缺點。它們都是估計將來的輸入數據特性相近,所以才可以透過統計方式提高期望的(expected)效能。但無論靜態或動態,都難以估計將來的輸入數據是否和profile的接近。動態的話,有可能可以進行適應性調整,但這種算法也不一定能帶來效能提升,反而會有profiling和JIT的開銷。

    三大JVM的JIT中的重要部分就是PGO。近年的一些研究就是试图找出更合适的profile模型和cost-benifit模型来估算优化哪部分代码、如何优化。动态优化的好处在支持动态加载的环境中会比较明显,因为这种环境中很可能无法静态完成whole program analysis,但却可以在运行时把当前状态当作“whole program”来分析并激进优化,而且仍然留下安全的退路。

    不算上pointer-to-member,C++的虚方法至少有两种方式调:通过实例调,或者是通过指针/引用调。例如说:

    A a;
    a.foo(); // 通过实例调
    A* p = &a;
    p->foo(); // 通过指针调
    

    即便foo()是虚方法,在a.foo()的形式下调用也必然是调到A的版本而不会是别的版本,因为可以安全的静态判定该实例一定是A类型的;而通过指针/引用调用则需要更多信息才可以安全的做静态单目标判定,这类才是“真”的方法调用。 顺带一提,C++也是有编译器对通过指针/引用调的虚方法的情况做激进优化的。可以参考篇96年的论文,作者正是参与了Sun的多个VM研发的人。

    CLR的GC老大Patrick Dussud说过托管运行时的一大好处是它可以较容易的保证类型安全。在需要动态分配空间的环境中,通过GC或其它自动内存管理方式来强制杜绝dangling pointer/double free之类的问题,禁止对内存的任意操作,是保证运行时类型安全的重要部分。 把经过高度tune过的不用GC的程序与普通的用GC的托管程序来比较性能是不合理的。GC是朋友而不是仆人,要用得高效一样需要tuning。

    虚拟机/运行时本身可以做的优化也很多,如果真觉得性能不够,那么完全可以在运行前在本地编译成native code,这和直接从源代码变成native code从结果上看没什么区别。但是现实是,很少有人去这么做,因为这么做往往只是节省了JIT的开销,对性能提高效果不大。

    现实是不少.NET程序有用NGEN的。至少.NET Framework自己用,微软写的不少用了.NET的程序也是安装时NGEN,以前我用的一个游戏脚本编辑器也是要求NGEN。对启动性能还是颇有影响的,对稳定性能则没什么实质好处,用普通NGEN预先编译的程序多半比运行时JIT的版本慢;当然这很大原因是NGEN实现得比较偷懒。

    那些“现在不快”的东西里很多都还有很多油水可榨。JavaScript引擎的演化是最好的例子,那些引擎们每隔几个月就能在SunSpider评测上把所需执行时间缩短一半是怎么可能的?与其说现在神乎其技,还是“以前太偷懒了”的说法更接近现实。IE9的JScript 9/Chakra引擎结构上其实跟client版HotSpot挺像的:混合模式的执行引擎中包含解释器与后台线程上工作的JIT编译器,JIT编译器也会做些现在看来很常规的优化,包括SSA形式的中间代码和相关常规优化、生成代码前的linear scan register allocation,之类的。跟以前纯解释器的JScript 5.6/5.7/5.8相比速度自然不可同日而语,就算是解释器本身JScript 9/Chakra也比以前的快多了。

    嗯顺带一提,纯解释器中快慢差异也可以很明显,在一些程序中Sun JDK 1.0系的解释器会比1.1系的解释器慢一倍。也不是JIT编译了就一定比纯解释快。首藤一幸提供过一组对比的例子,他最初写的简单JIT编译器的性能还比不上Sun JDK 1.1系列的解释器。LuaJIT的作者Mike Pall也表示LuaJIT 2里的解释器在一些场景里比LuaJIT 1的JIT编译后的代码还快。不要光看到某某VM有JIT编译器就以为它一定比纯解释器的方案快哦~☆

    说到JavaScript引擎,MSR最近宣传的SPUR项目值得关注。它的一个愿景就是用C#写一个浏览器,让SPUR能从JavaScript代码一直trace到JavaScript运行时内部,甚至到浏览器(DOM)一侧,极大的提高浏览器的整体性能。其中,CIL(或者说MSIL)作为一种便于分析的中间语言是实现这一愿景的重要组成部分(唔,虽然SPUR里用的不是“裸”的CIL而是改进过的、基于寄存器版的CIL,不过Herman Venter表示这个决定对性能的影响不大,只是因为有个研究员已经写了针对改进的CIL处理的程序,为了复用那些代码才把SPUR实现成这样的)。

    至于性能测试与结果的解读,这个就不提了。留给各位大牛们交流讨论把玩,呵呵。乱入结束。

  38. 路过
    183.37.78.*
    链接

    路过 2010-07-26 01:18:45

    是不是做了静态页??

  39. abc
    218.89.32.*
    链接

    abc 2010-09-18 20:28:24

    我只说理论,我的观点是虚拟机的效率未来必然会超过静态编译的效率。

    从理论上,通过JIT,托管代码比编译的本地代码性能可能会高很多。

    原因何在?在于输入上,所谓的优化,是有前提的,是对一定的输入集合进行优化的,比如一个函数f(x,y) = x+y 如果输入是自然数,那么x+y就要真正的执行加法,如果输入就一对,x=1,y=2,那么这个函数就被直接优化成3了,再也不会有运算。所以输入集合的减小,必然带来优化性能的提高。

    在编译代码的时候,我们假定的输入集合是S1,而同样的托管代码,虽然它的整体输入集合同样是S1,但是在某个时候,或者某个应用场合,或者某个用户那里,它的输入集合会是更小的一个集合S2,此时,托管代码会重新根据这个S2来进行重新编译,从而得到更高的效率。

  40. 火星大能猫
    222.92.17.*
    链接

    火星大能猫 2011-08-24 15:50:39

    飞信慢,那是因为winform的渲染效率的问题,如果用wpf开发,在win7下运行,绝对不慢.

已自动隐藏某些不合适的评论内容(主题无关,争吵谩骂,装疯卖傻等等),如需阅读,请准备好眼药水并点此登陆后查看(如登陆后仍无法浏览请留言告知)。

发表回复

登录 / 登录并记住我 ,登陆后便可删除或修改已发表的评论 (请注意保留评论内容)

昵称:(必填)

邮箱:(必填,仅用于Gavatar

主页:(可选)

评论内容(大于5个字符):

  1. Your Name yyyy-MM-dd HH:mm:ss

使用Live Messenger联系我