纵论WebAssembly,JS在性能逆境下召唤强援
webassembly的作用
webassembly是一種底層的二進制數據格式和一套可以操作這種數據的JS接口的統稱。我們可以認為webassembly的范疇里包含兩部分
wasm: 一種體積小、加載快并且可以在Web瀏覽器端運行的底層二進制數據格式,并且可以由C++等語言轉化而來
webassembly的操作接口:例如WebAssembly.instantiate就可以將一份wasm文件編譯輸出為JS能夠直接調用的模塊對象
打破性能瓶頸
一直以來,我們都比較關心JS的運行速度問題,V8引擎解決了絕大多數情況下遇到的問題,但是少數情況下我們進行大量本地運算的時候,仍然可能遇到性能瓶頸,需要優化,這個時候webassembly的作用就凸現出來了
webassembly項目的編碼流程
性能無強關的部分用JS編寫
性能強相關的,并且需要大量本地運算的部分,先用C++/Rust編寫,通過命令行工具轉化為wasm代碼后讓JS調用
玄學的webassembly性能提升
webassembly相對于純JS的性能提升是隨具體場景和條件的變化而變化的
當您使用WebAssembly時,不要總是期望得到20倍的加速。您可能只得到2倍的加速或者20%的加速。或者,如果您在內存中加載非常大的文件時,或者需要在WebAssembly和JavaScript之間進行大量通信時,那么速度可能會變慢。 作者:Robert 《Level Up With WebAssembly》一書的作者,同時也是一位生物信息學軟件工程師
參考鏈接
在上面的文章的作者Robert,做了這樣一個實驗,他使用 seqtk,一個用C編寫的評估DNA測序數據質量(通常用于操作這些數據文件)的軟件,去對比webassembly相對于普通JS帶來的性能提升
一.Robert的對比測試結果
下面是他的測試結果
第一步:運行序列分析軟件seqtk,對比性能:9倍提升
第二步:刪除不必要的printf輸出,對比性能:13倍提升
第三步:去除函數的重復調用后,對比性能:21倍提升
當然,上面的概括也許太過簡略,大家可以看看Robert的原文以得到更為詳細的認識
二.運行Fibonacci函數的性能對比
有位博主,對比了運行遞歸無優化的Fibonacci函數的時候,WebAssembly版本和原生JavaScript版本的性能差距,下圖是這兩個函數在值是45、48、50的時候的性能對比。
文章鏈接作者:detectiveHLH
三.IVweb的的性能對比測試
IVWeb團隊對長度不同的文本進行加密處理,對比webassembly相對于純JS的性能提升,結果發現
對于長文本(2M文本) 的密集計算,webassembly的性能提升很大
對于短文本("IVWEB")的密集計算,webassembly和純JS性能相差無幾
第一組測試:2M長文本100000 次加密處理
第二組測試:"ivweb"短字符加密100000 次
資料來源
從上面的資料中我們了解到,webassembly性能提升的確存在,但是這個提升的范圍是隨條件和場景而變化的,需要遵循一定的原則
webassembly的兼容
下面是我在can i use上查到的結果,可以看到在現代瀏覽器上兼容良好,覆蓋率達到88%。主要的問題在于IE瀏覽器不支持(IE11)
IE兼容解決方案
Internet Explorer 11 是最后一個占有很大的市場份額,但不支持wasm的瀏覽器。我們可以通過 binaryen 項目的 wasm2js 工具,將我們的 WebAssembly 編譯成 JavaScript,就可以獲得 IE11 的大部分支持了
實戰 WebAssembly
在瀏覽器中使用WebAssembly主要有兩種方式:
編寫Rust代碼,然后通過wasm-pack轉化成wasm代碼
編寫C/C++代碼,然后通過Emscripten轉化成wasm代碼
備注:Rust是一門高性能的系統編程語言
通過Rust接入WebAssembly
《Rust 和 WebAssembly 用例》
1.安裝rustup,初始化Rust環境,它會順帶安裝cargo等工具(相當于前端的Node安裝)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
2.安裝編譯工具wasm-pack(相當于前端的babel)
cargo install wasm-pack
3.創建一個文件夾,進入后運行下面代碼,初始化一個Rust 項目
cargo new --lib hello-wasm
初始化的文件夾如下所示
4.修改lib.rs,改為以下幾段Rust代碼,這段代碼的is_odd是一個判斷數字是否為奇數的方法
extern crate wasm_bindgen;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn is_odd(n: u32) -> bool {
n % 2 == 1
}
5.修改配置文件Cargo.toml
這個文件和我們的package.json有點像,我們就依樣畫葫蘆,這個文件大概要寫成下面這個樣子
[package]
name = "hello-wasm"
version = "0.1.0"
authors = ["作者名"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
crate-type = ["cdylib"]
[dependencies]
wasm-bindgen = "0.2"
備注
dependencies中必須要有wasm-bindgen這個依賴
同時還要指定crate-type = ["cdylib"],否則轉化不能成功
6.運行以下命令進行編譯轉化
wasm-pack build --scope [自己的名字]
// My Example
wasm-pack build --scope penghuwan
編譯開始
編譯成功后,新增了pkg文件夾和target文件夾
讓我們看看pkg文件夾下的文件有哪
7. 將包發布到npm
1.cd pkg
2.npm publish --access=public
8.安裝剛剛發布的wasm模塊,并通過webpack工具加載后,在瀏覽器運行以下代碼
const js = require("hello-wasm");
js.then(js => {
const num1 = js.is_odd(3);
const num2 = js.is_odd(4);
console.log(num1);
console.log(num2);
});
9.瀏覽器輸出
通過C/C++接入WebAssembly
1.首先要按照文檔下載編譯工具emscripten
https://emscripten.org/docs/getting_started/Tutorial.html
備注:如果沒有將source ./emsdk_env.sh寫入到啟動文件中的話,那么每次使用前都要在給定目錄下運行一遍
2.創建一個文件h.c,寫入以下代碼
#include <stdio.h>
int main(int argc, char ** argv) {
printf("Hello World");
}
3.用命令行編譯它
emcc h.c -s WASM=1 -o h.js
生成文件如下圖所示
4.運行生成的h.js,則可看到輸出了Hello World
WebAssembly相關的接口 API
看了上面的案例,你可能會覺得有些奇怪:怎么我們沒有涉及瀏覽器提供的webassembly的API呀?
其實是有的,只不過在工具編譯的時候自動幫忙填寫了一些API而已,我們看下上面從h.c編譯出來的h.js的一些片段就知道了
下面我們就來介紹下怎么手動去寫這些API
接口
>> WebAssembly.Instance
實例包含所有的 WebAssembly 導出函數 ,允許從JavaScript 調用 WebAssembly 代碼.
對象屬性
exports屬性: 一個對象,該對象包含從WebAssembly模塊實例導出的所有函數屬性
>> WebAssembly.Module
包含已經由瀏覽器編譯的無狀態 WebAssembly 代碼,可以高效地與 Workers 共享、緩存在 IndexedDB 中,和多次實例化。
對象屬性
exports屬性:一個數組,內容是所有已聲明的接口的描述。
imports屬性和:一個數組,內容是所有已聲明的引用的描述。
參考鏈接
方法
>> WebAssembly.instantiate
它是編譯和實例化 WebAssembly 代碼的主要方法
參數:包含你想編譯的wasm模塊二進制代碼的ArrayBuffer的類型實例
返回值: 一個Promise, resolve后的值如下所示
{
module: 一個被編譯好的 WebAssembly.Module 對象.
instance: 一個WebAssembly.Instance對象
}
Example
fetch('simple.wasm').then(response =>
response.arrayBuffer()
).then(bytes =>
WebAssembly.instantiate(bytes)
).then(result =>
result.instance.exports // exports是wasm中輸出的
);
webassembly的未來展望
多線程
SIMD(單指令流多數據流)
64位尋址
流式編譯(在下載的同時編譯 WebAssembly 文件)
分層編譯器
隱式 HTTP 緩存
參考文章
webassembly的使用場景及其限制
之前我們已經說到,webassembly適用于JS難以解決的大計算量的應用場景,如圖像/視頻編輯、計算機視覺,3D游戲等等。在這些場景下,webassembly能夠大限度地提高速度,彌補JS的缺陷和硬傷。
同時在另一方面,我們也需要認識到以下幾點:
其實在大多數場景下我們都不需要用到webassembly。因為V8等JS引擎的優化帶來了巨大的性能提升,已經足夠讓JS應對絕大多數的普通場景了,所以只有在以上的少數場景下,我們才需要做這種“二次提升”
和很多其他特性一樣,兼容性同樣是webassembly的一道坎,現代瀏覽器雖然支持度良好,但是在國內IE泛濫的特殊情況下, 這仍然是對webassembly的一個挑戰。不過在桌面應用上或者一些對兼容性要求較低的工具型網頁運用上,webassembly已經生根發芽,甚至能夠遍地開花。
webassembly的產品案例
設計工具Figma
一般情況下,為了使用速度,設計工具都會選擇Adobe等本地應用,而不會選擇瀏覽器網頁應用,而能夠同時打開十幾個畫板也沒有卡頓的Figma正在嘗試改變這一認知,webassembly讓它具有高效流暢的體驗
白鷺游戲引擎
白鷺游戲引擎是一套HTML5游戲開發解決方案,它衍生了開發莽荒紀同名手游、夢道、坦克風云的等游戲,而利用 WebAssembly,白鷺引擎讓游戲運行性能提升了300%。
OpenGL 圖形引擎Magnum
Magnum 是一款數據可視化 OpenGL 圖形處理引擎,也采用了WebAssembly支撐瀏覽器環境的應用
參考資料
from:https://www.cnblogs.com/penghuwan/p/11982601.html
總結
以上是生活随笔為你收集整理的纵论WebAssembly,JS在性能逆境下召唤强援的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: NodeMCU学习(三) : 进入网络世
- 下一篇: ViewPager + TabLayou