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