当前位置: 首页 > news >正文

使用GO编译wasm文件并在nodejs中使用

使用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.jsTinyGo:$(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 = 40await calcByPureGo(calcNum)
}main()// 执行效率: 计算fibonacci(40)耗时: 约1350ms

2. 原生GO直接计算

// 运行方式: go run fibonacci.go
package mainimport ("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 mainimport ("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方法注册成功")// 阻塞住 goroutinedone := 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.jsconst 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.gopackage mainimport "syscall/js"func main() {// 包装导出的函数作为闭包,并传递给js.FuncOffibFunc := 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文件

    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插件

    <!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
    
http://www.lryc.cn/news/123886.html

相关文章:

  • BM22 比较版本号
  • 【Java】Maven配置文件帮助文档(settings.xml 和 pom.xml)
  • 人脸识别技术应用安全管理规定(试行)
  • FPGA应用学习-----FIFO双口ram解决时钟域+asic样机的时钟选通
  • zabbix案例--zabbix监控Tomcat
  • Electron 应用实现截图并编辑功能
  • 前端= 结构(HTML)+ 样式(CSS)+ 行为(JavaScript)
  • Flink-网络流控及反压剖析
  • redis 和 mongodb 比较
  • Linux 主函数参数介绍
  • 资料分析(三)—— 基期、现期、人口、增长量
  • Java 正则表达式【匹配与分组基本原理】
  • ThreadLocal源码解析
  • RocketMQ 5.1.0 源码详解 | Producer 发送流程
  • 电脑ip地址怎么改 ip地址怎么改到别的城市
  • Android Studio实现列表展示图片
  • 每天一道leetcode:300. 最长递增子序列(动态规划中等)
  • 【无监督】2、MAE | 自监督模型提取的图像特征也很能打!(CVPR2022 Oral)
  • pytorch单机多卡后台运行
  • linux配置上网 linux adsl拨号上网设置
  • XML学习基础知识归纳(一)
  • 2023.8.14论文阅读
  • FL Studio for Windows-21.1.0.3713中文直装版功能介绍及系统配置要求
  • 基于网格变形的二维图像变形算法:C++实现与应用
  • 【数据结构】八大排序详解
  • VSCode如何设置高亮
  • 密钥大全ubuntu
  • Spring Task入门案例
  • 针对Android项目蓝牙如何学习
  • C++学习笔记总结练习:内存分配器编程实现