摘要:現在,我們將會剖析的工作原理,而最重要的是它和在性能方面的比對加載時間,執行速度,垃圾回收,內存使用,平臺訪問,調試,多線程以及可移植性。目前,是專門圍繞和的使用場景設計的。目前不支持多線程。
原文請查閱這里,略有改動,本文采用知識共享署名 4.0 國際許可協議共享,BY Troland。
本系列持續更新中,Github 地址請查閱這里。
這是 JavaScript 工作原理的第六章。
現在,我們將會剖析 WebAssembly 的工作原理,而最重要的是它和 JavaScript 在性能方面的比對:加載時間,執行速度,垃圾回收,內存使用,平臺 API 訪問,調試,多線程以及可移植性。
我們構建網頁程序的方式正面臨著改革-這只是個開始而我們對于網絡應用的思考方式正在發生改變。
首先,認識下 WebAssembly 吧WebAssembly(又稱 wasm) 是一種用于開發網絡應用的高效,底層的字節碼。
WASM 讓你在其中使用除 JavaScript 的語言以外的語言(比如 C, C++, Rust 及其它)來編寫應用程序,然后編譯成(提早) WebAssembly。
構建出來的網絡應用加載和運行速度都會非常快。
加載時間為了加載 JavaScript,瀏覽器必須加載所有文本格式的 js 文件。
瀏覽器會更加快速地加載 WebAssembly,因為 WebAssembly 只會傳輸已經編譯好的 wasm 文件。而且 wasm 是底層的類匯編語言,具有非常緊湊的二進制格式。
執行速度如今 Wasm 運行速度只比原生代碼慢 20%。無論如何,這是一個令人驚喜的結果。它是這樣的一種格式,會被編譯進沙箱環境中且在大量的約束條件下運行以保證沒有任何安全漏洞或者使之強化。和真正的原生代碼比較,執行速度的下降微乎其微。另外,未來將會更加快速。
更讓人高興的是,它具備很好的瀏覽器兼容特性-所有主流瀏覽器引擎都支持 WebAssembly 且運行速度相關無幾。
為了理解和 JavaScript 對比,WebAssembly 的執行速度有多快,你應該首先閱讀之前的 JavaScript 引擎工作原理的文章。
讓我們快速瀏覽下 V8 的運行機制:
左邊是 JavaScript 源碼,包含 JavaScript 函數。首先,源碼先把字符串轉換為記號以便于解析,之后生成一個語法抽象樹。
語法抽象樹是 JavaScript 程序邏輯在內存中的圖示。一旦生成圖示,V8 直接進入到機器碼階段。你基本上是遍歷樹,生成機器碼然后獲得編譯后的函數。這里沒有任何真正的嘗試來加速這一過程。
現在,讓我們看一下下一階段 V8 管道的工作內容:
現在,我們擁有 TurboFan ,它是 V8 的優化編譯程序之一。當 JavaScript 運行的時候,大量的代碼是在 V8 內部運行的。TurboFan 監視運行得慢的代碼,引起性能瓶頸的地方及熱點(內存使用過高的地方)以便優化它們。它把以上監視得到的代碼推向后端即優化過的即時編譯器,該編譯器把消耗大量 CPU 資源的函數轉換為性能更優的代碼。
它解決了性能的問題,但是缺點即是分析代碼及辨別哪些代碼需要優化的過程也是會消耗 CPU 資源的。這也即意味著更多的耗電量,特別是在手機設備。
但是,wasm 并不需要以上的全部步驟-它如下所示插入到執行過程中:
wasm 在編譯階段就已經通過了代碼優化。總之,解析也不需要了。你擁有優化后的二進制代碼可以直接插入到后端(即時編譯器)并生成機器碼。編譯器在前端已經完成了所有的代碼優化工作。
由于跳過了編譯過程中的不少步驟,這使得 wasm 的執行更加高效。
內存模型舉個栗子,一個 C++ 的程序的內存被編譯為 WebAssembly,它是整段連續的沒有空洞的內存塊。wasam 中有一個可以用來提升代碼安全性的功能即執行堆棧和線性內存隔離的概念。在 C++ 程序中,你有一塊動態內存區,你從其底部分配獲得內存堆棧,然后從其頂部獲得內存來增加內存堆棧的大小。你可以獲得一個指針然后在堆棧內存中遍歷以操作你不應該接觸到的變量。
這是大多數可疑軟件可以利用的漏洞。
WebAssembly 采用了完全不同的內存模型。執行堆棧和 WebAssembly 程序本身是隔離開來的,所以你無法從里面進行修改和改變諸如變量值的情形。同樣地,函數使用整數偏移而不是指針。函數指向一個間接函數表。之后,這些直接的計算出的數字進入模塊中的函數。它就是這樣運行的,這樣你就可以同時引入多個 wasm 模塊,偏移所有索引且每個模塊都運行良好。
更多關于 JavaScript 內存模型和管理的文章詳見這里。
內存垃圾回收你已經知曉 JavaScript 的內存管理是由內存垃圾回收器處理的。
WebAssembly 的情況有點不太一樣。它支持手動操作內存的語言。你也可以在 wasm 模塊中內置內存垃圾回收器,但這是一項復雜的任務。
目前,WebAssembly 是專門圍繞 C++ 和 RUST 的使用場景設計的。由于 wasm 是非常底層的語言,這意味著只比匯編語言高一級的編程語言會容易被編譯成 WebAssembly。C 語言可以使用 malloc,C++ 可以使用智能指針,Rust 使用完全不同的模式(一個完全不同的話題)。這些語言沒有使用內存垃圾回收器,所以他們不需要所有復雜運行時的東西來追蹤內存。WebAssembly 自然就很適合于這些語言。
另外,這些語言并不能夠 100% 地應用于復雜的 JavaScript 使用場景比如監聽 DOM 變化 。用 C++ 來寫整個的 HTML 程序是毫無意義的因為 C++ 并不是為此而設計的。大多數情況下,工程師用使用 C++ 或 Rust 來編寫 WebGL 或者高度優化的庫(比如大量的數學運算)。
然而,將來 WebAssembly 將會支持不帶內存垃圾回功能的的語言。
平臺接口訪問依賴于執行 JavaScript 的運行時環境,可以通過 JavaScript 程序來直接訪問這些平臺所暴露出的指定接口。比如,當你在瀏覽器中運行 JavaScript,網絡應用可以調用一系列的網頁接口來控制瀏覽器/設備的功能且訪問 DOM,CSSOM,WebGL,IndexedDB,Web Audio API 等等。
然而,WebAssembly 模塊不能夠訪問任何平臺的接口。所有的這一切都得由 JavaScript 來進行協調。如果你想在 WebAssembly 模塊內訪問一些指定平臺的接口,你必須得通過 JavaScript 來進行調用。
舉個栗子,如果你想要使用 console.log,你就得通過JavaScript 而不是 C++ 代碼來進行調用。而這些 JavaScript 調用會產生一定的性能損失。
情況不會一成不變的。規范將會為在未來為 wasm 提供訪問指定平臺的接口,這樣你就可以不用在程序中內置 JavaScript。
源碼映射當壓縮了 JavaScript 代碼的時候,你需要有合適的方法來進行調試。
這時候源碼映射就派上用場了。
大體上,源碼映射就是把合并/壓縮了的文件映射到未構建狀態的一種方式。當為生產環境進行代碼構建的時候,與壓縮和合并 JavaScript 一起,會生成源碼映射用來保存原始文件信息。當你想在生成的 JavaScript 代碼中查詢特定的行和列的代碼的時候,可以在源碼映射中進行查找以獲得代碼的原始位置。
由于沒有規范定義源碼映射,所以目前 WebAssembly 并不支持,但最終會有的(可能快了)。
當在 C++ 代碼中設置了斷點,就會看到 C++ 代碼而不是 WebAssembly。至少,這是 WebAssembly 源碼映射的目標吧。
多線程JavaScript 是單線程的。有很多方法來利用事件循環和使用在之前的文章中有提到的異步編程。
JavaScript 也使用 Web Workers 但是只有在極其特殊的情況下-大體上,可以把任何可能阻塞 UI 主線程的密集的 CPU 計算移交給 Web Worker 執行以獲得更好的性能。但是,Web Worker 不能夠訪問 DOM。
目前 WebAssembly 不支持多線程。但是,這有可能是接下來 WebAssembly 要實現的。Wasm 將會接近實現原生的線程(比如,C++ 風格的線程)。擁有真正的線程將會在瀏覽器中創造出很多新的機遇。并且當然,會增加濫用的可能性。
可移植性現在 JavaScript 幾乎可以運行于任意的地方,從瀏覽器到服務端甚至在嵌入式系統中。
WebAssembly 設計旨在安全性和可移植性。正如 JavaScript 那樣。它將會在任何支持 wasm 的環境(比如每個瀏覽器)中運行。
WebAssembly 擁有和早年 Java 使用 Applets 來實現可移植性的同樣的目標。
WebAssembly 使用場景WebAssembly 的最初版本主要是為了解決大量計算密集型的計算的(比如處理數學問題)。最為主流的使用場景即游戲-處理大量的像素。
你可以使用你熟悉的 OpenGL 綁定來編寫 C++/Rust 程序,然后編譯成 wasm。之后,它就可以在瀏覽器中運行。
瀏覽下(在火孤中運行)-http://s3.amazonaws.com/mozil...。這是運行于Unreal engine(這是一個可以用來開發虛擬現實的開發套件)中的。
另一個合理使用 WebAssembly (高性能)的情況即實現一些處理計算密集型的庫。比如,一些圖形操作。
正如之前所提到的,wasm 可以有效減少移動設備的電力損耗(依賴于引擎),這是由于大多數的步驟已經在編譯階段提前處理完成。
未來,你可以直接使用 WASM 二進制庫即使你沒有編寫編譯成它的代碼。你可以在 NPM 上面找到一些開始使用這項技術的項目。
針對操作 DOM 和頻繁使用平臺接口的情況 ,使用 JavaScript 會更加合理,因為它不會產生額外的性能開銷且它原生支持各種接口。
在 SessionStack 我們一直致力于持續提升 JavaScript 的性能以編寫高質量和高效的代碼。我們的解決方案必須擁有閃電般的性能因為我們不能夠影響用戶程序的性能。一旦把 SessionStack 整合進網絡應用或網站的生產環境,它會開始記錄所有的一切:所有的 DOM 變化,用戶交互,JavaScript?異常,堆棧追蹤,失敗的網絡請求和調試數據。所有的這一切都是在生產環境中產生且沒有影響到產品的任何交互和性能。我們必須極大地優化我們的代碼并且盡可能地讓它異步執行。
我們不僅僅有庫,還有其它功能!當你在 SessionStack 中重放用戶會話,我們必須渲染問題產生時用戶的瀏覽器所發生的一切,而且我們必須重構整個狀態,允許你在會話時間線上來回跳轉。為了使之成為可能,我們大量地使用異步操作,因為 JavaScript 中沒有比這更好的替代選擇了。
有了 WebAssembly,我們就可以把大量的數據計算和渲染的工作移交給更加合適的語言來進行處理而把數據收集和 DOM 操作交給 JavaScript 進行處理。
番外篇打開 webassembly 官網就可以在頭部醒目地看到顯示它兼容的瀏覽器。分別是火孤,Chrome,Safari,IE Edge。點開 learn more 可以查看到這是于 2017/2/28 達成一致推出瀏覽器預覽版。現在各項工作開始進入實施階段了,相信在未來的某個時刻就可以在生產環境使用它了。官網上面介紹了一個 JavaScript 的子集 asm.js。另外,這里有一個 WebAssembly 和 JavaScript 進行性能比對的測試網站。
本系列持續更新中,Github 地址請查閱這里。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/95007.html
摘要:前端每周清單專注前端領域內容,以對外文資料的搜集為主,幫助開發者了解一周前端熱點分為新聞熱點開發教程工程實踐深度閱讀開源項目巔峰人生等欄目。背后的故事本文是對于年之間世界發生的大事件的詳細介紹,闡述了從提出到角力到流產的前世今生。 前端每周清單專注前端領域內容,以對外文資料的搜集為主,幫助開發者了解一周前端熱點;分為新聞熱點、開發教程、工程實踐、深度閱讀、開源項目、巔峰人生等欄目。歡迎...
摘要:為了方便大家共同學習,整理了之前博客系列的文章,目前已整理是如何工作這個系列,可以請猛戳博客查看。以下列出該系列目錄,歡迎點個星星,我將更友動力整理理優質的文章,一起學習。 為了方便大家共同學習,整理了之前博客系列的文章,目前已整理 JavaScript 是如何工作這個系列,可以請猛戳GitHub博客查看。 以下列出該系列目錄,歡迎點個星星,我將更友動力整理理優質的文章,一起學習。 J...
摘要:前端每周清單專注前端領域內容,以對外文資料的搜集為主,幫助開發者了解一周前端熱點分為新聞熱點開發教程工程實踐深度閱讀開源項目巔峰人生等欄目。利用降低三倍加載速度自推出之后,很多開發者都開始嘗試在小型項目中實踐,不過尚缺大型真實案例比較。 前端每周清單專注前端領域內容,以對外文資料的搜集為主,幫助開發者了解一周前端熱點;分為新聞熱點、開發教程、工程實踐、深度閱讀、開源項目、巔峰人生等欄目...
摘要:圖表中的比例并不代表真實情況下的確切比例情況。解析當到達瀏覽器時,源代碼就被解析成了抽象語法樹。解析過后抽象語法樹就變成了中間代碼叫做字節碼,提供給引擎編譯。目前為止,不支持垃圾回收。這就是為什么在大多數情況下,同一個任務比表現更好的原因。 作者:Lin Clark 編譯:胡子大哈 翻譯原文:http://huziketang.com/blog/posts/detail?postId...
閱讀 1181·2021-09-22 15:24
閱讀 2285·2019-08-30 15:44
閱讀 2615·2019-08-30 10:55
閱讀 3354·2019-08-29 13:25
閱讀 1638·2019-08-29 13:09
閱讀 1391·2019-08-26 14:05
閱讀 1379·2019-08-26 13:58
閱讀 1984·2019-08-26 11:57