使用GO编译wasm文件并在nodejs中使用
安装Go相关环境
# 安装GO
# mac使用homebrew安装
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
brew install go
# vi ~/.bashrc, 添加如下内容
export GOPATH=$HOME/go
export PATH=$PATH:/usr/local/go/bin:$GOPATH/bin
# 确认安装成功
source ~/.bashrc
go version
// 取消联网下载库
export GO111MODULE=off
# 安装tinygo
brew tap tinygo-org/tools
brew install tinygo
获取wasm_exec.js
wasm_exec.js是Go WebAssembly相关的JavaScript文件,用于加载和初始化WebAssembly运行时环境,创建一个全局的GO对象,已经JavaScript和WebAssembly之间进行通讯
该文件在GO和TinyGO中使用的略有不同, 使用不同方式打的包需要使用对应的wasm_exec.js文件,安装对应的包后可以以下位置找到它们,将其复制到项目目录下
GO:
$(go env GOROOT)/misc/wasm/wasm_exec.js
TinyGo:
$(tinygo env TINYGOROOT)/targets/wasm_exec.js
斐波那契数计算的n种方法
1. 原生JS直接计算
// 执行方式: node fibonacci.js
// 原生JS环境运行
async function calcByJS(calcNum) {
console.log('-----------原生JS运行环境-----------');
console.time("原生JS耗时");
const resultJS = calcFibonacci(calcNum);
// console.log('原生JS计算结果:', resultJS);
console.timeEnd("原生JS耗时");
console.log('-----------原生JS运行环境-----------\n');
}
// JS计算斐波那契函数
function calcFibonacci(n) {
if (n == 0) {
return 0;
} else if (n == 1) {
return 1;
} else {
return calcFibonacci(n - 1) + calcFibonacci(n - 2);
}
}
async function main() {
const calcNum = 40
await calcByPureGo(calcNum)
}
main()
// 执行效率: 计算fibonacci(40)耗时: 约1350ms
2. 原生GO直接计算
// 运行方式: go run fibonacci.go
package main
import (
"fmt"
"time"
)
func fibonacci(n uint) uint {
if n == 0 {
return 0
} else if n == 1 {
return 1
} else {
return fibonacci(n-1) + fibonacci(n-2)
}
}
func main() {
start := time.Now()
n := uint(40)
fibonacci(n)
fmt.Println("纯GO运算时间:", time.Since(start).Milliseconds(),"ms")
}
// 执行效率: 计算fibonacci(40)耗时: 约470ms
3. GO打包成WASM包由JS进行调用
- GO文件
package main
import (
"syscall/js"
"fmt"
// "time"
)
//export fibonacci
func fibonacci(this js.Value, args []js.Value) interface{} {
n := args[0].Int()
result := calculateFibonacci(n)
return js.ValueOf(result)
}
func calculateFibonacci(n int) int {
if n <= 1 {
return n
}
return calculateFibonacci(n-1) + calculateFibonacci(n-2)
}
func main() {
// 注册fibonacci方法
js.Global().Set("fibonacci", js.FuncOf(fibonacci))
// 调用fibonacci方法
// start := time.Now()
// n := int(40)
// fibonacci(js.Undefined(), []js.Value{js.ValueOf(n)})
// fmt.Printf("纯GO耗时:%vms \n", time.Since(start).Milliseconds())
fmt.Println("fibonacci方法注册成功")
// 阻塞住 goroutine
done := make(chan struct{}, 0)
<-done
}
-
打包命令:
GOOS=js GOARCH=wasm go build -o ./fibonacci.wasm fibonacci.go
-
调用was_exec.js文件完成WebAssembly运行时环境初始化
// Copyright 2021 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. "use strict"; globalThis.require = require; globalThis.fs = require("fs"); globalThis.TextEncoder = require("util").TextEncoder; globalThis.TextDecoder = require("util").TextDecoder; globalThis.performance = { now() { const [sec, nsec] = process.hrtime(); return sec * 1000 + nsec / 1000000; }, }; const crypto = require("crypto"); const path = require("path") globalThis.crypto = { getRandomValues(b) { crypto.randomFillSync(b); }, }; require("./wasm_exec"); // 加载Go编写的wasm文件 async function getWasmOfGo(wasmPath) { const go = new Go(); go.argv = [wasmPath]; go.env = Object.assign({ TMPDIR: require("os").tmpdir() }, process.env); go.exit = process.exit; const { instance } = await WebAssembly.instantiate(fs.readFileSync(path.join(__dirname, wasmPath)), go.importObject); // 执行 Go WebAssembly 实例 go.run(instance) } module.exports = getWasmOfGo
-
JS调用wasm文件执行
// node fibonacci.js const getWasmOfGo = require('./go_init') // GO计算斐波那契函数 async function calcByGo(num) { console.log('-----------Go打包成wasm包运行-----------'); const wasmPath = 'fibonacci.wasm'; console.time('GO-wasm总耗时') console.time('wasm初始化总耗时') await getWasmOfGo(wasmPath); console.timeEnd('wasm初始化总耗时') // 调用导出的函数并传递参数 console.time('GO-wasm计算耗时') const result = fibonacci(num); // console.log('GO计算结果1', result); console.timeEnd('GO-wasm计算耗时'); console.timeEnd('GO-wasm总耗时') console.log('-----------Go打包成wasm包运行-----------\n'); } calcByGo(40) // module.exports = calcByGo -----------Go打包成wasm包运行----------- fibonacci方法注册成功 wasm初始化总耗时: 436.300ms GO-wasm计算耗时: 2341.956ms GO-wasm总耗时: 2778.486ms -----------Go打包成wasm包运行-----------
4. TinyGo打包成wasm包运行
-
Go文件
// tinygo_fibonacci.go package main import "syscall/js" func main() { // 包装导出的函数作为闭包,并传递给js.FuncOf fibFunc := js.FuncOf(func(this js.Value, args []js.Value) interface{} { n := args[0].Int() result := fibonacci(n) return result }) // 将包装后的函数赋值给全局对象的某个属性(例如window) js.Global().Set("fibonacci", fibFunc) select {} } // Fibonacci 函数 func fibonacci(n int) int { if n <= 1 { return n } return fibonacci(n-1) + fibonacci(n-2) }
-
打包命令
tinygo build -o tinygo_fibonacci.wasm -target wasm tinygo_fibonacci.go
-
调用was_exec.js文件完成WebAssembly运行时环境初始化
// Copyright 2021 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. "use strict"; globalThis.require = require; globalThis.fs = require("fs"); globalThis.TextEncoder = require("util").TextEncoder; globalThis.TextDecoder = require("util").TextDecoder; globalThis.performance = { now() { const [sec, nsec] = process.hrtime(); return sec * 1000 + nsec / 1000000; }, }; const crypto = require("crypto"); const path = require("path") globalThis.crypto = { getRandomValues(b) { crypto.randomFillSync(b); }, }; require("./wasm_exec_tiny"); // 加载Go编写的wasm文件 async function getWasmOfGo(wasmPath) { const go = new Go(); go.argv = [wasmPath]; go.env = Object.assign({ TMPDIR: require("os").tmpdir() }, process.env); go.exit = process.exit; const { instance } = await WebAssembly.instantiate(fs.readFileSync(path.join(__dirname, wasmPath)), go.importObject); // 执行 Go WebAssembly 实例 go.run(instance) } module.exports = getWasmOfGo
-
JS文件文章来源:https://www.toymoban.com/news/detail-648240.html
const getWasmOfGo = require('./tinygo_init') // GO计算斐波那契函数 async function calcByTinyGo(num) { const wasmPath = 'tinygo_fibonacci.wasm'; console.log('-----------TinyGo打包成wasm包运行-----------'); console.time('GO总耗时') console.time('wasm初始化总耗时') await getWasmOfGo(wasmPath); console.timeEnd('wasm初始化总耗时') // 调用导出的函数并传递参数 console.time('GO计算耗时') const result = fibonacci(num); // console.log('GO计算结果', result); console.timeEnd('GO计算耗时'); console.timeEnd('GO总耗时') console.log('-----------TinyGo打包成wasm包运行-----------'); } module.exports = calcByTinyGo -----------TinyGo打包成wasm包运行----------- wasm初始化总耗时: 19.252ms GO计算耗时: 633.676ms GO总耗时: 653.138ms -----------TinyGo打包成wasm包运行-----------
5. html调用GO打的wasm包
需启动http服务打开才能调用, 可使用vscode 的LiveServer插件文章来源地址https://www.toymoban.com/news/detail-648240.html
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>TinyGo WebAssembly Demo</title> <script src="wasm_exec.js"></script> <!-- <script src="wasm_exec_tiny.js"></script> --> <script src="//aeu.alicdn.com/waf/interfaceacting220819.js"></script> <script src="//aeu.alicdn.com/waf/antidomxss_v640.js"></script> <script> const go = new Go(); WebAssembly.instantiateStreaming(fetch('fibonacci.wasm'), go.importObject) .then(result => { go.run(result.instance); }) .catch(err => { console.error(err); }); // 在页面上定义fibonacci函数,并调用WebAssembly模块中的fibonacci函数 function fibonacci(n) { const wasmFibonacci = window.fibonacci; if (wasmFibonacci === undefined || typeof wasmFibonacci !== 'function') { console.error('fibonacci function not found'); return; } return wasmFibonacci(n); } async function test() { console.time('Web计算耗时'); const result = await fibonacci(40) console.log(result); console.timeEnd('Web计算耗时'); } // JS计算斐波那契函数 async function calcByJS(calcNum) { console.time("原生JS耗时"); const resultJS = calcFibonacci(calcNum); // console.log('原生JS计算结果:', resultJS); console.timeEnd("原生JS耗时"); } function calcFibonacci(n) { if (n == 0) { return 0; } else if (n == 1) { return 1; } else { return calcFibonacci(n - 1) + calcFibonacci(n - 2); } } </script> </head> <body> <h1>TinyGo WebAssembly Demo</h1> <button onclick="test()">Calculate Fibonacci</button> <button onclick="calcByJS(40)">Calculate Fibonacci By JS</button> </body> </html> GO的wasm:Web计算耗时: 3429.2451171875 ms TinyGO的wasm: Web计算耗时: 963.423095703125 ms 浏览器JS耗时: 原生JS耗时: 1429.368896484375 ms
到了这里,关于使用GO编译wasm文件并在nodejs中使用的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!