V8是谷歌開源的一個高效能JavaScript引擎,用 C++ 實現,並用在谷歌的開源瀏覽器Chrome裡。
為什麼V8非常快,哪種方案讓V8達到這種速度?發現其中秘密是一件有趣的事情。
面向物件、設計樣式與效能
有些C/C++開發者有些奇怪的觀念。他們認為使用面向物件和設計樣式會降低程式的效能。但V8證明瞭這種觀念是錯誤的。V8的實現使用了許多設計樣式,但依然非常高效。
下麵列出V8中使用的兩個樣式:
工廠樣式
當Javascript引擎執行一個指令碼時,引擎為遇到的每個變數、函式或陣列都建立一個實體。JSObject是所有這些物件的父物件。
下麵列出了所有繼承自JSObject的類:
V8實現了一個工廠類來建立這些物件,該類中的Factory::NewJsObject就是用來建立這些物件的。
下麵列出了所有使用該類/方法的方法。
V8引擎中的類並沒有直接使用這個工廠類,而是添加了另一層封裝,透過Heap類呼叫該工廠類。
訪問者樣式:
維基百科上這樣解釋觀察者樣式:
觀察者設計樣式是將演演算法和演演算法處理的物件分開的一種方式。這種分離可以在不修改結構本身的情況下,將新的操作新增到已有的物件結構上。這是一條遵循open/closed準則的方式。
與工廠樣式相似,訪問者樣式也為實現添加了封裝層。這樣讓其程式碼更加可讀且可維護。
V8原始碼中許多類都實現了訪問者樣式。
即使V8開發者必須最佳化執行效率,他們也不在乎新增到程式碼中的封裝層。使用設計樣式和新增一些C++的機制會增加一些封裝,所以的確會對效率有影響。但這對效率的影響僅佔一小部分,更多的影響來自該應用使用的設計決策。
V8中針對執行效率方面的設計決策
1. 隱藏類和快速屬性訪問。
JavaScript是一種動態程式語言:可以在物件執行時為物件新增或刪除熟悉。這意味著很容易改變物件的屬性。
JSFunction和JSValue的父類都是JSObject,JSFunction用來表示一個javascript函式,JSValue用來表示一個javascript值。但沒有繼承自JSObject的類,用以表示Function或Value這樣的Class。許多JavaScript引擎使用詞典型別的資料結構來儲存這些物件的熟悉,訪問每個屬性都需要動態查詢並解析屬性在記憶體中的位置。
這種方式導致JavaScript在訪問物件變數的屬性時,比在Java或Smalltalk中要慢。在這些語言中,實列變數分配的位置是固定的,即由編譯器根據物件的類定義中的佈局,在該物件在記憶體中的位置加上固定的偏移位置。因此訪問這些屬性僅僅是記憶體上的讀取或儲存,而這種操作通常只需一條指令。
V8使用隱藏類概念來降低訪問JavaScript屬性所消耗的時間。V8不使用動態查詢來訪問屬性,而是在幕後建立隱藏類。
2. 動態生產機器碼
在首次執行時,V8就將JavaScript原始碼直接編譯成機器碼,沒有中間位元組碼,沒有直譯器。屬性訪問由行內的快取程式碼處理,V8執行時可能會有其他機器指令修改這些快取程式碼。
3. 高效的垃圾收集器
在執行過程中,V8會重新獲得廢棄物件的記憶體,即垃圾回收。為了保證擁有較快的物件分類、較短的垃圾回收停頓,以及沒有記憶體碎片。V8使用了停頓、分代、精確垃圾回收器。這意味著V8使用了:
-
在垃圾回收迴圈期間停止程式的執行。
-
在大多數垃圾迴圈中,只處理物件堆的一部分。這最大化降低了停頓對應用的影響。
-
記錄所有物件和指標在記憶體中的位置,避免了將物件作為指標識別而導致的記憶體洩漏。
結論:
出於效率因素而不使用面向物件或設計樣式,這是一個錯誤的觀念。這樣只會獲得數毫秒的最佳化,卻失去了程式碼的可讀性和可維護性。
原文出處:www.codergears.com
譯文出處:伯樂線上 – Daetalus