雖然專注在後端技術上,對前端只有初學者左右的程度,但還是多少對前端的技術很感興趣
而且這個技術對後端相對方便的,如果可以在前端技術上有更多精進的能力,多少可以解決更多問題

WebAssembly 簡介

WebAssembly 是一個以二進制表示的低階語言,可以在瀏覽器上運行,由於是二進制所以運行速度肯定會快點

特點則是說可以接近原生的效能,我個人觀點則是可以突破某些狀況下的限制和效能的提升,所以也有可能變慢囉!要謹慎使用

並且 2019 年時 WebAssembly正式成為W3C推薦的網頁應用標準

WebAssembly 與 JS 共存,輔助加強 JS 產生更多的應用並提升性能

目前可以使用 C/C++GoRust 透過 Emscripten 編譯成 WebAssembly

WebAssembly - Go

Go 1.11 開始支援 WebAssembly 可以透過 Go 來進行開發

首先需要將 wasm_exec.js 複製到需專案目錄中,稍候需要 js 調用 wasm 檔案

1
cp "$(go env GOROOT)/misc/wasm/wasm_exec.js" .

Sample

寫個簡單的程式,先練練手培養的感覺

1
2
3
4
5
6
7
package main

import "fmt"

func main(){
    fmt.Println("Hello, WebAssembly!")
}

然後對此進行編譯,參數則是 GOOS=jsGOARCH=wasm,稍候就可以透過 js 來呼叫 wasm

1
GOOS=js GOARCH=wasm go build -o main.wasm

HTML

撰寫一個 index.html 引入 wasm_exec.js,然後透過 * JavaScript 使用 main.wasm 中的 function

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
<html>
	<head>
		<meta charset="utf-8"/>
		<script src="wasm_exec.js"></script>
		<script>
			const go = new Go();
				* WebAssembly.instantiateStreaming(fetch("main.wasm"), go.importObject).then((result) => {
				go.run(result.instance);
			});
		</script>
	</head>
	<body>
		Test!!
	</body>
</html>

目錄樹

.
├── go.mod
├── go.sum
├── index.html
├── main.go
├── main.wasm
└── wasm_exec.js

測試

測試需要一個簡易的 web server,官方推薦使用 goexec 來方便搭建 http 協議處理

1
2
# install goexec: go get -u github.com/shurcooL/goexec
go get -u github.com/shurcooL/goexec

然後在專案的環境中執行以下命令,就可以很快速的建立起簡單的 Web Server,可以拿來測試使用

1
goexec 'http.ListenAndServe(`:8080`, http.FileServer(http.Dir(`.`)))'

透過瀏覽器訪問 http://127.0.0.1:8080,然後在瀏覽器中使用 F12 開啟開發者工具

在 Console 介面中觀看是否出現 Hello, WebAssembly!

進階測試

那就來寫一個加解密的功能看是否可以正常呼叫,之前在寫 Pairing-Based Cryptography 相關的程式的時候

當時實在是找不到 JS 的 Library 可以使用,所以當時弄了一個 API 進行呼叫,來測試看看是否能轉成 wasm

Demo

首先呢,想要將函式可以在瀏覽器中給 JS呼叫,這裡就要 import "syscall/js"

主要是讓 Go 可以與 JS 之間的變數型態可以相互溝通,並且讓 JS 可以呼叫 Go 的函式

然後設定 demo 的函式讓 JS 可以呼叫

1
2
3
4
5
import "syscall/js"

func main() {
	js.Global().Set("demo", js.FuncOf(demo))
}

再來就是將參數可以傳入 Go 的函式中進行使用,透過 args []js.Value 將參數傳入

而我這邊只傳入 明文m 這個參數並轉換成字串型別,所以只有 args[0].String()

在透過加密解密的過程,這邊則省略,若有興趣完整的程式碼在 github.com/cody0704/WebAssembly-example-go

是一個簡單 PBC 的加密、簽章的基本算法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
func demo(thie js.Value, args []js.Value) interface{} {
	m := args[0].String()

	...

	if encrypted, err := encrypt([]byte(m), z.Marshal()[:32]); err != nil {
		fmt.Println(err)
	} else {
		fmt.Printf("CIPHER KEY: %s\n", string(z.Marshal()[:32]))
		fmt.Printf("ENCRYPTED: %s\n", encrypted)

		if decrypted, err := decrypt(encrypted, temp.Marshal()[:32]); err != nil {
			log.Println(err)
		} else {
			log.Printf("DECRYPTED: %s\n", decrypted)
			return decrypted
		}
	}

	return "Decrypt Failed"
}

然後一樣進行編譯,然後再運行簡易測試用的 Web Server

1
2
3
GOOS=js GOARCH=wasm go build -o main.wasm

goexec 'http.ListenAndServe(`:8080`, http.FileServer(http.Dir(`.`)))'

再度訪問 http://127.0.0.1:8080,並呼叫 demo 這個函式,看是否能正常使用

應用

看起來 WebAssembly 比較是用來進行比較複雜的運算,大多數應用在 遊戲、密碼加密、影音等等

畢竟大多數的功能還是可以由 JS 完成,貌似只有追求效能時,才會評估是否可以使用 WebAssembly

也可以將某些已經完成的功能可以快速移植到 wasm 使用,就不用再開發 JS 的版本

或者是有些 JS 本來就沒人開發的功能,直接編譯使用

效能

不過這樣可能也無法評斷出效能上的差異,takahirox.github.io/WebAssembly-benchmark

分別提供 JS 和 WebAssembly 的寫法,並且提供了各種的情境,可以從網頁上直接測試

當然不是全部都是 WebAssembly 比較快,還是需要適當的使用,以下是我測試出來的結果

- JavaScript WebAssembly JavaScript/WebAssembly
collisionDetection 456.3445 ms 517.0255 ms 0.8826
Fibonacci 456.3445 ms 517.0255 ms 2.7034
ImageConvolute 50.5980 ms 40.1635 ms 1.2598
ImageGrayscale 1.4198 ms 3.2808 ms 0.4328
ImageThreshold 7.6906 ms 7.0060 ms 1.0977
MultiplyInt 947.4215 ms 213.8170 ms 4.4310
MultiplyDouble 964.2590 ms 577.9550 ms 1.6684
MultiplyIntVec 65.6780 ms 97.9795 ms 0.6703
MultiplyDoubleVec 75.5460 ms 170.6345 ms 0.4427
QuicksortInt 635.2805 ms 413.1235 ms 1.5377
QuicksortDouble 309.5190 ms 262.5270 ms 1.1790
SumInt 139.2075 ms 163.4175 ms 0.8519
SumDouble 83.0160 ms 162.4150 ms 0.5111
VideoConvolute 31.5505 ms 22.4300 ms 1.4066
VideoGrayscale 0.9350 ms 2.7695 ms 0.3376
VideoMarkerDetection 5.9095 ms 5.9825 ms 0.9878
VideoThreshold 3.4204 ms 4.2052 ms 0.8134

結論

從性能在各種情境下的測試,可以大致上從表中來決定在什麼狀況下來使用 wasm 會提升性能

否則如果一味使用 wasm 搞不好會挖坑把自己埋了

然而還是可以看出有很多方便的用法,不過也擔心會變得很雜亂

不曉得哪些是呼叫 JS 哪些是從 wasm 呼叫的,需要再整理資料

Ref

  1. github.com/WebAssembly/design
  2. WebAssembly
  3. 【WebAssembly應用案例直擊】它們都在用WebAssembly
  4. WebAssembly正式成為W3C推薦的網頁應用標準
  5. Package js
  6. github.com/cody0704/WebAssembly-example-go
  7. takahirox.github.io/WebAssembly-benchmark