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