|
最近博客園上在炒關(guān)于C#性能的問題,其實(shí)應(yīng)該說是.NET性能的問題,其中某位仁兄提出,他希望C#能夠直接編譯為原生代碼,而不是在CLR這樣一個(gè)托管運(yùn)行時(shí)上執(zhí)行,因?yàn)樘摂M機(jī)啊,JIT什么的性能差。后來發(fā)到TL上以后,也有朋友認(rèn)為,“基于虛擬機(jī)的語言都是大公司為了利益在推動(dòng),說白了就是政治”,因此“對(duì)C#提高性能的建議感到可笑,因?yàn)樗緛砭筒皇怯脕黹_發(fā)高性能程序的”,再有,“C、C++已經(jīng)明確不和這些后進(jìn)爭(zhēng)所謂的‘容易開發(fā)’的頭銜”,那么其他語言為什么要和C++它們比較性能呢?我是托管運(yùn)行時(shí),或者虛擬機(jī)的忠實(shí)擁護(hù)者,這里談一下我在這方面的看法。
我并不反對(duì)編譯為原生代碼的語言,尤其是C語言,它的意義在于提供了一種對(duì)硬件完全控制的手段,對(duì)硬件提供了一種最直接的抽象,幾乎可以映射到最終流程控制方式,因此無可替代。C++作為C語言的超集,提供了更豐富的抽象能力(如面向?qū)ο蠛湍0婊?,只是語言本身過于復(fù)雜,超過了以我的智商可以承受的范圍,因此我學(xué)了幾次都沒怎么學(xué)會(huì),現(xiàn)在更是忘得差不多了。不過我認(rèn)為,越來越多的語言會(huì)構(gòu)建在托管平臺(tái)上,而不是直接編譯成原生代碼。因?yàn)橐粋€(gè)統(tǒng)一的托管運(yùn)行時(shí)會(huì)帶來很多好處。
首先,統(tǒng)一運(yùn)行時(shí)提供了跨平臺(tái)的能力,Java便是一典型。.NET上有mono,使用也很廣泛,也有不少Unity3D,Gnome DO等成功案例。Novell,包括其他一些公司也在銷售基于mono的商業(yè)產(chǎn)品(如MonoTouch及Infragistics的ASP.NET Controls組件),我本身也在兩年多前就在生產(chǎn)環(huán)境上使用了mono,您現(xiàn)在看到的這個(gè)博客也是基于Ubuntu Server、mono 2.6 、Apache以及微軟開源的ASP.NET MVC 2構(gòu)建的。雖說從某些層面(如API兼容性)上說,mono的跨平臺(tái)性遠(yuǎn)不如Java平臺(tái),但它也是一個(gè)比較成熟的執(zhí)行環(huán)境,并具備相當(dāng)程度的跨平臺(tái)能力——尤其是在mono上開發(fā),MS .NET上運(yùn)行的時(shí)候(雖然我不建議這么做)。如今支持mono的產(chǎn)品、類庫數(shù)不勝數(shù),我時(shí)常調(diào)侃道,“如果您的產(chǎn)品不支持mono,還真不好意思和人打招呼”。只可惜,不少人都用一些“不是親娘生的”類似的調(diào)調(diào)來否定mono,在我看來沒有經(jīng)過調(diào)查研究的看法只能屬于“臆斷”,而且更是一種FUD了。
即便退一步來說,我們不“跨操作系統(tǒng)”吧。有人說,.NET就支持Windows么,何必搞個(gè)虛擬機(jī),還JIT那么麻煩。但事實(shí)上,“跨平臺(tái)”并非指的是簡(jiǎn)單的“跨操作系統(tǒng)”,而是“跨執(zhí)行環(huán)境”,如Silverlight。事實(shí)上,跨計(jì)算機(jī)體系結(jié)構(gòu)本身也是種跨平臺(tái)(當(dāng)然,操作系統(tǒng)其實(shí)已經(jīng)進(jìn)行了一定的統(tǒng)一抽象了)。因此,虛擬機(jī)的目的,是為上層執(zhí)行體抽象出了統(tǒng)一的運(yùn)行環(huán)境——這其實(shí)還是跨平臺(tái),這平臺(tái)不僅僅是指操作系統(tǒng),整體運(yùn)行環(huán)境之間的差異也是運(yùn)行時(shí)所“抽象”的一部分。比如在并發(fā)環(huán)境中,不同CPU架構(gòu)的流水線上的亂序方式不一樣,同樣的代碼執(zhí)行的效果就可能不同。最典型的例子,便是JVM之前的內(nèi)存一致性模型控制的比較寬松,導(dǎo)致經(jīng)典的double check模式在某些CPU上是會(huì)失敗的?,F(xiàn)在Java標(biāo)準(zhǔn)也變得嚴(yán)格了,和.NET CLR一樣避免了Store Reordering。這意味著在某些CPU上,會(huì)在特定的地方加上Memory Barrier保證執(zhí)行效果的一致性。在我看來這是更好的可移植性。C++或是C語言等實(shí)現(xiàn)“可移植性”的方式,往往是通過為不同環(huán)境提供不同的編譯器,生成不同的結(jié)果,而且會(huì)使用“宏”等方式,在代碼里寫出有平臺(tái)針對(duì)性的代碼。
有了統(tǒng)一的運(yùn)行時(shí),也可以讓多語言互操作更為容易,如果沒有JVM或CLR,就很難像現(xiàn)在這么輕松地在Scala/Java/Jython/JRuby,或是C#/F#/IronPython/IronRuby,甚至是未來的語言之間進(jìn)行直接地互操作,更難做到“無縫集成”了。在合適的場(chǎng)景下選用合適的語言,是提高生產(chǎn)力的重要手段。如果沒有JVM平臺(tái),就很難使用Scala來代替Java這樣的劣質(zhì)語言,而現(xiàn)在Scala便能夠保證充分利用Java平臺(tái)上類庫等積淀。
有了“多語言”,那么便會(huì)引出虛擬機(jī)的另一個(gè)作用:讓語言實(shí)現(xiàn)者和虛擬機(jī)實(shí)現(xiàn)者的工作可以分離開來,各自優(yōu)化。虛擬機(jī)的實(shí)現(xiàn)者可以盡力優(yōu)化虛擬機(jī)的各種表現(xiàn),為虛擬機(jī)加入各種優(yōu)化措施,而無需讓虛擬機(jī)的上層語言分別調(diào)整。例如JVM性能提高之后,Scala、Java、Jython、JRuby等語言的性能都可以提高,否則各種語言分別優(yōu)化的代價(jià)太高了(當(dāng)然某些情況還是需要特殊對(duì)待的)。要說這方面的實(shí)例,在.NET平臺(tái)上有個(gè)開源的組件DLR(動(dòng)態(tài)語言運(yùn)行時(shí)),是在CLR上提供編寫動(dòng)態(tài)語言的統(tǒng)一輔助類庫。以前它有個(gè)缺點(diǎn),便是啟動(dòng)速度比較慢,因?yàn)閯?dòng)態(tài)代碼的JIT耗時(shí)較長,而動(dòng)態(tài)語言的很多場(chǎng)景是“即用即拋”的。后來,DLR增加了一個(gè)優(yōu)化策略,便是一開始直接對(duì)抽象語法樹直接解釋執(zhí)行,而執(zhí)行多次之后才使用后臺(tái)線程將代碼JIT成原生代碼。有了如此改進(jìn),基于DLR的動(dòng)態(tài)語言,如IronRuby和IronPython的啟動(dòng)速度都提高了。
虛擬機(jī)/運(yùn)行時(shí)本身可以做的優(yōu)化也很多,如果真覺得性能不夠,那么完全可以在運(yùn)行前在本地編譯成native code,這和直接從源代碼變成native code從結(jié)果上看沒什么區(qū)別。但是現(xiàn)實(shí)是,很少有人去這么做,因?yàn)檫@么做往往只是節(jié)省了JIT的開銷,對(duì)性能提高效果不大。在不同環(huán)境中,此外還有各種優(yōu)化,比如使用解釋執(zhí)行,而不是JIT以次節(jié)省內(nèi)存消耗,或是在運(yùn)行時(shí)回收J(rèn)IT的代碼(印象中在.NET Compact Framework里有這樣的策略,求詳情),或是在運(yùn)行時(shí)根據(jù)代碼邏輯進(jìn)行二次編譯。下面會(huì)談一個(gè)例子。另一種典型的優(yōu)化,一直在研究卻還沒有真正實(shí)現(xiàn)的,虛擬機(jī)便是“自動(dòng)并行”。關(guān)于這點(diǎn),Anders在上次的演講中多次提到過,要實(shí)現(xiàn)這點(diǎn)還需要有各種支持,如聲明式編程,提供“無副作用”標(biāo)記,甚至在語言級(jí)別的支持等等。
之前那位朋友提到,C/C++已經(jīng)明確不與后進(jìn)語言比生產(chǎn)力了,后進(jìn)語言也沒辦法和它們比較性能。對(duì)這個(gè)觀點(diǎn)我持保留意見,因?yàn)榛谔摂M機(jī)的做法,其優(yōu)化空間還有很多,理論上也可以做到更為徹底,在許多情況下性能完全有超越靜態(tài)編譯語言的可能。這是許多人的看法,而事實(shí)也是如此,在某些場(chǎng)景下Java的性能也已經(jīng)超過了C++。如回到上面的二次編譯優(yōu)化,對(duì)于性能優(yōu)化也大有好處。舉一個(gè)簡(jiǎn)單的例子,面向?qū)ο笳Z言會(huì)出現(xiàn)很多虛方法調(diào)用(尤其是在符合一些關(guān)于設(shè)計(jì)的最佳實(shí)踐時(shí),如“基于抽象編程”),調(diào)用需方法需要查方法表找方法入口,最普通的做法就是必須每次根據(jù)對(duì)象的實(shí)際類型查找方法表,找到地址,然后調(diào)用。偽代碼如下:
根據(jù)實(shí)際類型找到函數(shù)入口
調(diào)用
NET技術(shù):為什么我支持托管運(yùn)行時(shí)(虛擬機(jī)),轉(zhuǎn)載需保留來源!
鄭重聲明:本文版權(quán)歸原作者所有,轉(zhuǎn)載文章僅為傳播更多信息之目的,如作者信息標(biāo)記有誤,請(qǐng)第一時(shí)間聯(lián)系我們修改或刪除,多謝。