天天看點

簡記V8與SquirrleFish/Tamarin的對比點

剛才還是忍不住到V8的官網讀了些文檔,想看看到底它的技術特點是什麼。

V8官網:[url=http://code.google.com/p/v8/]Google V8 JavaScript Engine[/url]

設計概念的文檔:[url=http://code.google.com/apis/v8/design.html]Design Elements[/url]

有人初試V8的經過:[url]http://d.hatena.ne.jp/amachang/20080903/1220405193[/url]

V8是BSD許可證的,比較自由,友善用在各種項目中。

它是一個相當快的JavaScript實作。根據其設計概念的文檔,它的高性能主要來自:

[list][*]快速的屬性通路

[*]動态機器碼生成

[*]高效的垃圾收集[/list]

其中前兩點應該是緊密相關聯的。在JavaScript引擎的實作中,V8特别就特别在“隐藏類”(hidden class)的使用。Hidden class的概念可以到那篇文檔去看,解釋得比較生動。簡單來說,對于某個對象,每次屬性的數量(或類型?這個得回頭仔細研究下。更新:V8雖然後來的版本裡可以跟蹤屬性的類型,但這并不影響hidden class transition)發生改變時,虛拟機就會新建立一個對應的隐藏類,記錄下其中各個屬性的相對偏移量,并将原本的隐藏類與新的隐藏類之間建立一個關聯。當再次有同一類型的對象建立時則不會重複建立隐藏類,而能複用前面已經出現的隐藏類。

其它JavaScript引擎(以及許多其它腳本語言的實作)一般使用某種形式的關聯容器來儲存所有變量;通常叫字典,也會被稱為映射表或者關聯數組,典型的實作方式是哈希表或者紅黑樹等。對象中的成員變量(下面将稱為“屬性”)一般也是這樣與某個對象關聯在一起的。通路某個對象的某個屬性時就需要動态查詢這樣的關聯容器,是不可忽視的開銷。

V8則不使用關聯容器來儲存屬性,而是采用更接近于靜态編譯的類的形式,将對象中每個屬性的相對偏移量記下來,在生成機器碼時直接把偏移量寫到指令中。這樣,通路某個對象的某個屬性就隻需要很少量的指令就能完成,比起關聯容器的方式高效許多。

V8使用動态機器碼生成,先把要執行的JavaScript代碼直接編譯為機器碼,而不使用位元組碼也不通過解釋的方式來執行。這樣在執行一些被大量重複執行的代碼時效果會特别好。與之相對,Apple Webkit的JavaScriptCore/[url=http://webkit.org/blog/189/announcing-squirrelfish]SquirrelFish[/url]是先将JavaScript編譯到位元組碼,然後以純解釋的方式執行位元組碼;Mozilla/Adobe的[url=http://www.mozilla.org/projects/tamarin/]Tamarin[/url]則是先将JavaScript編譯到位元組碼,然後以即時編譯(JIT)的方式執行。SquirrelFish與Tamarin的位元組碼設計思路又不一樣:前者是基于寄存器的,而後者是基于棧的。一般認為基于寄存器形式的位元組碼比較快,而基于棧形式的位元組碼比較小,總之也是各有特點。

與“隐藏類”相關的是,V8會預先猜測某段代碼中的對象對應的隐藏類,如果命中的話就能直接用已經生成到機器碼裡的偏移量;如果沒有命中,則使用實際的隐藏類中的資訊來修改已生成的機器碼。這樣,如果多次執行中對象的“實際類型”都與猜測一樣,執行速度就可以非常的快。

Mozilla SpiderMonkey裡的新JIT編譯器,TraceMonkey則通過别的方式來提高執行速度。通過記錄代碼執行的路徑,當發現某條執行路徑回到了某個早先經過的節點,就發現了一個“trace”;然後對trace花時間做重點優化,編譯為高效的機器碼。與HotSpot JVM不同的是,trace不是以函數(或者說方法)為機關的,可以在更小的範圍内做精确的優化,減少不必要的優化開銷。

在垃圾收集器方面,V8采用的是一個兩代的分代式準确垃圾收集器。相對的,JavaScriptCore的GC繼承自KJS,沒記錯的話是一個沒有分代的準确M&S式(mark-and-sweep)垃圾收集器;Tamarin使用的則是Adobe提供的[url=http://developer.mozilla.org/En/MMgc]MMgc[/url],主要算法是“延遲的引用計數”(DRC,Deferred Reference Counting),外加增量式保守M&S的垃圾收集器。這三種實作方式各有特點,實際效果要比較起來恐怕比較難。準确式的垃圾收集器必須知道堆的位置和布局,是以難以做成通用的;保守式收集器則可以做得很通用,著名的[url=http://www.hpl.hp.com/personal/Hans_Boehm/gc/]Boehm GC[/url]是個典型的例子。但保守式收集器有潛在的記憶體洩漏的危險,因為可能會把并不是對象指針的資料識别為指針,使本來應該已經可以釋放的空間無法被釋放。

===========================================================================

V8中使用了下列第三方庫:

[quote] - Jscre, located under third_party/jscre. This code is copyrighted

by the University of Cambridge and Apple Inc. and released under a

2-clause BSD license.

- Dtoa, located under third_party/dtoa. This code is copyrighted by

David M. Gay and released under an MIT license.

- Strongtalk assembler, the basis of the files assembler-arm-inl.h,

assembler-arm.cc, assembler-arm.h, assembler-ia32-inl.h,

assembler-ia32.cc, assembler-ia32.h, assembler.cc and assembler.h.

This code is copyrighted by Sun Microsystems Inc. and released

under a 3-clause BSD license.[/quote]

JSCRE我沒怎麼聽說過,不知道跟PCRE(Perl Compatible Regular Expressions)的關系是怎樣的。在JSCRE源碼的目錄裡看到了"pcre"的字樣,不過詳細情況還是以後再看看好了。Apple Webkit裡的JavaScriptCore應該是直接用了PCRE的吧?還是說我看漏了……

(更新:JSCRE就是Apple Webkit改造過的版本的PCRE)

===========================================================================

今天Mozilla方面也沒忘記對Chrome/V8的釋出作出反應。Brendan Eich,JavaScript的創始者,在其blog上發表了一篇相關評論:

[url=http://weblogs.mozillazine.org/roadmap/archives/2008/09/tracemonkey_update.html]Brendan Eich: TraceMonkey Update[/url]

[quote="Brendan Eich"][img]http://rednaxelafx.iteye.com/upload/picture/pic/21171/c2cbf917-ce6a-356e-ad25-3ae36907ec8b.png[/img][/quote]

(向右多的是TraceMonkey比較快,向左多的是V8比較快)

可以看到在Brendan做的SunSpider測試中,TraceMonkey在遞歸密集的程式中速度會比V8慢許多,而在正規表達式等方面則比V8快。據Brendan稱,TraceMonkey的開發時間還不長,遞歸方面的trace還沒多少進度,是以在這部分是比較慢的。但下一步将會解決這個問題。

P.S. KJS也不再用tree-walker方式實作解釋器了,改用了位元組碼解釋器:[url=http://www.kdedevelopers.org/node/3323]Say hello to KJS/Frostbyte -40.9° and Icemaker[/url]

Frostbyte分支的KJS是新的位元組碼解釋器。這個版本的KJS在把JavaScript源碼編譯到位元組碼的過程中有做優化,指令選擇、代碼生成和解釋器的主循環都由叫做Icemaker的工具來生成。很多其它編譯器是在生成native code的codegen才會做這種指令選擇,KJS選擇在這個位置做感覺挺依賴解釋器自身的性能⋯

繼續閱讀