編程語言的發(fā)展趨勢及未來方向(4):動態(tài)語言
這是Anders Hejlsberg(不用介紹這是誰了吧)在比利時TechDays 2010所做的開場演講。由于最近我在博客上關(guān)于語言的討論比較多,出于應(yīng)景,也打算將Anders的演講完整地聽寫出來。在上一部分中,Anders談及了聲明式編程的另一個重要組成部分:函數(shù)式編程,并使用.NET平臺上的函數(shù)式編程語言F#進行了演示。在這一部分中,Anders討論了動態(tài)語言及JavaScript的相關(guān)內(nèi)容,“動態(tài)性”也是Anders眼中編程語言的發(fā)展趨勢之一。
本文引用地址:http://m.butianyuan.cn/article/201704/346374.htm如果沒有特別說明,所有的文字都直接翻譯自Anders的演講,并使用我自己的口語習(xí)慣表達出來,對于Anders的口誤及反復(fù)等情況,必要時在譯文中自然也會進行忽略。為了方便理解,我也會將視頻中關(guān)鍵部分進行截圖,而某些代碼演示則會直接作為文章內(nèi)容發(fā)表。
(聽寫開始,接上篇)
我下面繼續(xù)要講的是動態(tài)語言,這也是我之前提到的三種趨勢之一。
我還是嘗試著去找到動態(tài)語言的定義,但是你也知道……一般地說,動態(tài)語言是一些不對編譯時和運行時進行嚴格區(qū)分的語言。這不像一些靜態(tài)編程語言,比如C#,你先進行編譯,然后會得到一些編譯期錯誤,稍后再執(zhí)行,而對于動態(tài)語言來說這兩個階段便混合在一起了。我們都熟悉一些動態(tài)語言,比如JavaScript,Python,Ruby,LISP等等。
動態(tài)語言有一些優(yōu)勢,而靜態(tài)語言也有著另一些優(yōu)勢,這也是兩個陣營爭論多年的內(nèi)容。老實講,我認為結(jié)果不是兩者中的任意一個,它們都有各自十分重要的優(yōu)點,而長期來看,我認為結(jié)果應(yīng)該是兩者的雜交產(chǎn)物,我認為在語言發(fā)展中也可以看到這樣的趨勢,這兩部分內(nèi)容正在合并。
許多人認定動態(tài)語言執(zhí)行起來很慢,也沒有類型安全等等。我想在這里觀察并比較一下,究竟是什么原因會讓靜態(tài)語言和動態(tài)語言在這方面有不同的性質(zhì)。這里有一段有趣的代碼,它的語法在JavaScript和C#里都是正確的,這樣我們便能比較兩種語言是如何處理這段代碼的。
首先我們把它看作是一段C#代碼,它只是用for循環(huán)把一堆整數(shù)相加,你肯定不會這么做,這只是一個示例。在C#中,當(dāng)我們使用var關(guān)鍵字時,它表示“請為我推斷這里的類型”,所以在這里a和i的類型都是int。
這斷代碼在執(zhí)行的時候,這兩個值都是32位整數(shù),而for循環(huán)只是簡單的使用ADD指令即可,執(zhí)行起來自然效率很高。
但如果從JavaScript或是動態(tài)語言的角度來看……或者說對于動態(tài)類型的語言來說,var只代表了“一個值”,它可以是任意類型,我們不知道它究竟是什么。所以當(dāng)我們使用var a或var i時,我們只是定義了兩個值,其中包含了一個“類型”標(biāo)記,表明在運行時它是個什么類型。在這里它是一個int,因此包含了存儲int值的空間。但有些時候,例如要存儲一個double值,那么可能便需要更多的空間,還可能是一個字符串,于是便包含一個引用。
所以兩者的區(qū)別之一便是,表示同樣的值在動態(tài)語言中會有一些額外的開銷,代價較高。而在如今的CPU中,“空間”便等于“速度”,所以較大的值便需要較長時間進行處理,這里便損失了一部分效率。
在JavaScript中,我們?nèi)绻幚韆加i,那么便不僅僅是一個ADD指令。首先它必須查看兩個變量中的類型標(biāo)記,然后根據(jù)類型選擇合適的相加操作。于是再去加載兩個值,然后再進行加法操作。這里還需要進行越界檢查,因為在JavaScript中一旦越界了便要使用double,等等。很明顯在這里也有許多開銷。一般來說,動態(tài)語言是使用解釋器來執(zhí)行的,因此還有一些解釋器需要的二進制碼。你把這些開銷全部加起來以后,便會發(fā)現(xiàn)執(zhí)行代碼時需要10倍到100倍的開銷。
不過由于近幾年來出現(xiàn)的一些動態(tài)虛擬機或引擎,目前這些情況改善了許多。比方說,這是傳統(tǒng)的情況(上圖左),如在IE 6或IE 7里使用的非常緩慢的解釋器。目前的情況是,大部分的JavaScript引擎使用了JIT編譯器(上圖中),于是便省下了解釋器的開銷,這樣性能損失便會減小至3到10倍。而在過去的兩三年間,JIT編譯器也變得越來越高效,瀏覽器中新一代的適應(yīng)性JIT編譯器(上圖右),如TraceMonkey,V8,還有如今微軟在IE 9中使用的Chakra引擎。這種適應(yīng)性的JIT編譯器使用了一部分有趣的技術(shù),如Inline Caching、Type Specialization、Hidden Classes、Tracing等等,它們可以將開銷降低至2到3倍的范圍內(nèi),這種效率的提升可謂十分神奇。
在我看來,JavaScript引擎可能已經(jīng)接近了性能優(yōu)化的極限,我們在效率上可以提升的空間已經(jīng)不多。不過我同樣認為,如今JavaScript語言的性能已經(jīng)足夠快了,完全有能力統(tǒng)治Web客戶端。
有人認為,JavaScript從來不是一種適合進行大規(guī)模編程的語言。如今也有一些有趣的工具,如Google Web Tookit,在微軟Nikhil Kothari也創(chuàng)建了Script#,讓你可以編寫C#或Java代碼,然后將代碼編譯成JavaScript,這就像是將JavaScript當(dāng)作是一種中間語言。Google Wave的所有代碼都用GWT寫成,它的團隊堅持認為用JavaScript不可能完成這樣的工作,因為復(fù)雜度實在太高了。如今在這方面還有一些有趣的開發(fā)成果,我不清楚什么時候會結(jié)束。不過我認為,這些都不算是大規(guī)模的JavaScript開發(fā)方案,而編寫C#或Java代碼再生成JavaScript的方式也不能算是完全正確的做法。我們可以關(guān)注這方面的走向。
在.NET 4.0的運行時進行動態(tài)編程時,我們引入了一個新功能:動態(tài)語言運行時??梢赃@樣理解,CLR的目的是為靜態(tài)類型的編程語言提供一個統(tǒng)一的框架或編程模型,而DLR便是在.NET平臺上為動態(tài)語言提供了統(tǒng)一的編程模型。CLR本身已經(jīng)有一些支持動態(tài)編程能力,如反射,Emit等等。不過在.NET上實現(xiàn)動態(tài)語言的時候,總會一遍又一遍地去實現(xiàn)某些功能,還有如動態(tài)語言如何與靜態(tài)語言進行交互,這些都由DLR來提供。DLR的特性包含了,如表達式樹、動態(tài)分發(fā)、Call Site緩存,這可以提高動態(tài)代碼的執(zhí)行效率。
在.NET 4.0中我們使用了DLR,不僅僅是IronPython和IronRuby,還有C# 4和VB.NET 10,它們使用DLR實現(xiàn)動態(tài)分發(fā)功能。因此我們共享了語言的動態(tài)能力實現(xiàn)方式,于是這些語言之間可以輕松地進行交互。同樣我們可以與其他多樣性的技術(shù)進行交互,例如使用JavaScript操作Silverlight的DOM,或是與Ruby、Python代碼溝通,甚至用來控制Office等自動化服務(wù)。
評論