Firefox中JavaScript和WebAssembly間函式呼叫更加順暢了,Mozilla在最新版本Firefox Nightly中最佳化了JavaScript到WebAssembly,以及從WebAssembly到JavaScript兩個方向的呼叫,主要的手段有兩個,減少簿記以及消除中介。

Mozilla對Firefox執行WebAssembly的效能,持續做出改進,在Firefox 58中加入了串流編譯以及分層編譯技術,讓電腦版的Firefox每秒可以編譯30到60MB的WebAssembly程式碼,也就是說程式碼編譯的速度將能快過網路下載速度,使得程式碼下載結束時,也能幾乎同時完成應用程式編譯工作。

而現在把效能改善的目標,轉向函式呼叫上,Mozilla提到,他們一開始的工作是讓JavaScript和WebAssembly可以相互合作,而這件事並不難,只是這兩種語言的函式呼叫效率不高,甚至慢的出名。Mozilla在Firefox Nightly上,最佳化了從JavaScript到WebAssembly,和從WebAssembly到JavaScript兩個方向的函式呼叫,同時也加速了WebAssembly呼叫瀏覽器內建函數的呼叫。

最佳化的工作可以歸結為兩個主要做法,除了減少簿記 ,以減少不必要的組織堆疊框(Stack Frame)工作,還移除中介,以最直接的途徑操作函式。每種瀏覽器都有自己的JavaScript引擎,之前只要處理JavaScript,現在還多了WebAssembly,即便只處理JavaScript,引擎都必須處理兩種不同的語言位元組碼和機器碼。

部分JavaScript程式碼會進到直譯器中,被轉成位元組碼,這比起JavaScript更接近機器碼,執行的速度相對來說比較快,但又不如機器碼。而部分被大量呼叫的函式則會經由即時編譯器(JIT)直接轉換為機器碼,這過程不需要經過直譯器。

這兩種語言就像是兩塊不同的大陸,引擎要執行不同的語言就像是在大陸之間跳躍,但跳躍時需要記錄一些必要訊息,而為了組織這個工作,引擎需要一個類似文件夾概念的地方,存放跳躍旅行的堆疊框,隨著累積文件夾數量會越來越多,而每切換到不同的大陸,引擎就會啟用一個不必要的文件夾,新增文件夾必會經C++進行彈跳床(Trampoline),這會明顯的增加執行成本。

Mozilla讓經過JIT的JavaScript和WebAssembly的程式碼,都使用相同的文件夾來修正這個問題,就像是把兩塊大陸合併在一起,不再需要在大陸間跳躍,因而讓WebAssembly到JavaScript的呼叫,幾乎和JavaScript呼叫JavaScript的函式一樣快。

另外,除了WebAssembly到JavaScript呼叫需要提高效率外,反向更是需要加速。即便是JIT-ed JavaScript已經和WebAssembly使用相同的語言,但是語言的處理習慣仍然不同,像是JavaScript會使用Boxing來處理動態類型,但WebAssembly是靜態型別,因此當引擎從JavaScript獲取經裝箱(Boxing)的參數,並傳遞給WebAssembly函式時,WebAssembly會不知道如何使用。

因此引擎在將裝箱參數傳遞給WebAssembly之前,需要經過拆箱手續,而這個動作也需要經C++彈跳床手續。進入中介的過程需要極大的成本,特別是處理這些並不複雜的工作,因此Mozilla的策略是消除這個中間人,來加速整個過程。Firefox採用C++執行的進入存根(Entry Stub )代碼,使其可以從JIT程式碼直接進行呼叫,當引擎從JavaScript轉到WebAssembly時,進入存根將能直接拆箱並將他們放置到正確的位置,而這種方式則消除了經C++彈跳床的程序。

這個方式就像是小抄一樣,引擎使用小抄因此不再需要轉至C++,相反的,還可以在JavaScript和WebAssembly呼叫的過程間將值拆箱。這讓JavaScript呼叫WebAssembly函式的速度變快,甚至在許多情況下,快過JavaScript呼叫JavaScript函式。

而在WebAssembly呼叫瀏覽器內建函數時,同樣會有C++彈跳床所造成的效能低落問題,Mozilla為這些內建函式建立快速路徑,當引擎發現正在呼叫的函式屬於內建函式,便會使用快速路徑來處理呼叫,這些快速路徑就像是在大陸間建造快速強橋樑一樣。

這一系列的改進讓JavaScript和WebAssembly之間的溝通更為有效率,也讓WebAssembly呼叫內建函式更為快速,雖然目前只有Firefox獲得這些改進,但使用者可以期待其他瀏覽器也會很快的跟進。

熱門新聞

Advertisement