一起谈.NET技术,为什么我支持托管运行时(虚拟机)

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

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

  首先,统一运行时提供了跨平台的能力,Java便是一典型。.NET上有mono,使用也很广泛,也有不少Unity3D,Gnome DO等成功案例。Novell,包括其他一些公司也在销售基于mono的商业产品(如MonoTouch及Infragistics的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-GuidedOptimizations:

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后的代码都可以回收再动态生成,这种优化是静态编译难以比拟的。

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

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

时间: 2024-08-29 09:22:49

一起谈.NET技术,为什么我支持托管运行时(虚拟机)的相关文章

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

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

一起谈.NET技术,Log4Net 全方位跟踪程序运行

前端日子自己写了一个简单的日志跟踪程序,现在目前正在做的一个项目中使用以便于跟踪程序异常和运行状况,但是被否认了!可能是没有权威性,于是自己总结了一下Log4net日志跟踪系统,这里分享大家学习一下.当然写这个文章的人太多了,这里不是做任何攀比,只是简单的分享供有需要的人. 一. Log4Net 简介 Log4net 是 Apache 下一个开放源码的项目,它是Log4j 的一个克隆版.我们可以控制日志信息的输出目的地.Log4net中定义了多种日志信息输出模式.  在做项目的时候令我最头疼的是

一起谈.NET技术,ASP.NET的运行原理与运行机制

当一个HTTP请求到服务器并被IIS接收到之后,IIS首先通过客户端请求的页面类型为其加载相应的.dll文件,然后在处理过程中将这条请求发送给能够处理这个请求的模块.在ASP.NET 3.5中,这个模块叫做HttpHandler(HTTP处理程序组件),之所以.aspx文件可以被服务器处理,就是因为在服务器端有默认的HttpHandler专门处理.aspx文件.IIS在将这条请求发送给能够处理这个请求的模块之前,还需要经过一些HttpModule的处理,这些都是系统默认的Modules(用于获取

一起谈.NET技术,.NET中通过代理实现面向方面编程(AOP)

上篇文章我说到了在代码中可以利用泛型委托来封装异常处理,这样可以让程序看起来更加清晰,要想完成功能需要调用者调用指定的工厂方法才行,但要想改变某些程序员的编码习惯我想是一件比较困难的事情.有朋友说利用委托来实现异常处理并不算是真正意义上的AOP,因为传统的AOP并不需要客户端做代码结构的变更,最多也就是配置上的问题.但在.net中要想实现AOP,我想最方便的实现机制要属代理机制了,但只要利用代理,在性能上就会造成一定的影响. 如果开发过分布式服务,像remotion,wcf等,消息都是它们通信的

一起谈.NET技术,案例分析:Silverlight在中国人寿的应用

笔者自2003年首次听到Macromedia公司提起RIA(富互联网应用)一词到现在整整7年了.一度被认为是互联网应用趋势的RIA经历了7年之痒,但仍然没有在互联网上得到大规模普及,特别是企业应用就更加少见.做个不恰当的比喻,传统基于Html的应用就像互联网应用中的绿叶一样,而RIA技术由于酷炫的用户体验效果就像是美丽的花朵.现在开心网和腾讯QQ等商业应用中已经运用了RIA技术在其社交网站中得到应用,但这毕竟还是少数,大多数互联网应用特别是企业级应用仍然选择传统高稳定性与高响应能力的Html应用

一起谈.NET技术,分清“语言/规范”以及“平台/实现”,以及跨平台.NET开发

在许多年前,"语言"就等同于"平台",例如C,C++以及最早的Ruby和Python等等.但是随着技术发展,出现了一些通用的平台,例如.NET和Java,逐渐这些平台上的语言也越来越多.再后来,某些语言在不同平台上的实现也越来越多,事情也变得有些复杂.技术在发展,但是从目前社区的讨论中,我发现许多朋友的观念还没有跟上.简单地说,如今的观念,一定要从"语言即平台"切换成"语言及平台",当分清"语言"和&quo

一起谈.NET技术,《Effective C#中文版:改善C#程序的50种方法》读书笔记

从去年找工作以来,都没什么时间写博客[找工作的体会:建议以后有自己开公司的IT人一定要找IT专业人员做HR,好多公司的HR并不能真正发掘人才,他们形成了太多的偏见,如在学校期间学不了什么东西.只看学校有多少奖励等.真正钻研技术的人才不会追求虚无的东西],其实这本书我都借了好久,一直没有系统的看,所以趁这两天好好看看,顺便总结了一些要点,给那些需要这方面知识而又没有太多时间的IT人一个快速的学习机会....如果要深入学习,请购买该书. 一.用属性代替可访问的字段 1..NET数据绑定只支持数据绑定

一起谈.NET技术,.NET 4九大新特性 FrameWork达到新境界

本文将向您介绍.NET框架4中的主要功能和改进特征.请注意,本文中并没有提供有关这些新功能的综合信息,并随时可能更改. 请注意,.NET框架4引入了一个改进的安全模式.有关该内容的更多的信息,请参阅文章<.NET框架4中的安全变化>. 具体来说,本文中将介绍.NET框架4的如下一些新功能和改进特征: 应用程序兼容性和部署 内核新功能及改进 托管扩展框架 并行计算 网络编程 Web开发 客户端开发 数据 通信和工作流 一.应用程序兼容性和部署 除了一些在安全.标准遵从.正确性.可靠性及性能等方面

一起谈.NET技术,WebForm:毒药还是利器?

一.Webform的诞生及运行机制,web开发带来的革命性变化 九十年代中期,Internet崭露头角.为了进军Web应用程序行业,微软开发了Active ServerPages(ASP).ASP是开发Web页面的一种快速.简便的方式.ASP页面由一个页面组成,其中包含了标记和语言的混合.ASP的强大之处在于,在页面发送给终端用户的Web浏览器之前,可以在页面上包含在Web服务器上执行的VBScript或JScript代码指令.这是创建动态Web页面的一种简单方式,动态Web页面可以根据开发人员