当前位置: 首页 > 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
    

相关文章:

使用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 &#xff5e;/.bashrc&#xff0c; 添加如下内容 e…...

BM22 比较版本号

一.双指针遍历截取 import java.util.*;public class Solution {/*** 代码中的类名、方法名、参数名已经指定&#xff0c;请勿修改&#xff0c;直接返回方法规定的值即可** 比较版本号* param version1 string字符串 * param version2 string字符串 * return int整型*/public …...

【Java】Maven配置文件帮助文档(settings.xml 和 pom.xml)

文章目录 1. settings.xml1.1 localRepository1.2 interactiveMode1.3 offline1.4 pluginGroups1.5 proxies1.6 servers1.7 mirrors1.8 profiles1.9 activeProfiles 2. pom.xml2.1 本项目信息2.2 父项目信息2.3 prerequisites2.4 issueManagement2.5 ciManagement2.6 inception…...

人脸识别技术应用安全管理规定(试行)

近年来&#xff0c;人脸识别技术不断成熟&#xff0c;已大量应用于治安管理、金融支付、门禁考勤等诸多领域&#xff0c;极大便捷了公众生活。然而&#xff0c;人脸识别技术在得到广泛应用的同时&#xff0c;仍存在一些不规范现象。人脸识别因其技术特点&#xff0c;涉及公众敏…...

FPGA应用学习-----FIFO双口ram解决时钟域+asic样机的时钟选通

60m写入异步ram&#xff0c;再用100M从ram中读出 写地址转换为格雷码后&#xff0c;打两拍和读地址判断是否空产生。相反读地址来判断是否满产生。 分割同步模块 asic时钟的门控时钟&#xff0c;fpga是不推荐采用门控时钟的&#xff0c;有很多方法移除fpga的时钟选通。 如果是a…...

zabbix案例--zabbix监控Tomcat

目录 一、 部署tomcat 二、配置zabbix-java-gateway 三、配置zabbix-server 四、配置zabbix-web界面 一、 部署tomcat tar xf apache-tomcat-8.5.16.tar.gz -C /usr/local/ ln -sv /usr/local/apache-tomcat-8.5.16/ /usr/local/tomcat cd /usr/local/tomcat/bin开启JMX…...

Electron 应用实现截图并编辑功能

Electron 应用实现截图并编辑功能 Electron 应用如何实现截屏功能&#xff0c;有两种思路&#xff0c;作为一个框架是否可以通过框架实现截屏&#xff0c;另一种就是 javaScript 结合 html 中画布功能实现截屏。 在初步思考之后&#xff0c;本文优先探索使用 Electron 实现截屏…...

前端= 结构(HTML)+ 样式(CSS)+ 行为(JavaScript)

前端开发确实涵盖了行为&#xff08;JavaScript&#xff09;、样式&#xff08;CSS&#xff09;和结构&#xff08;HTML&#xff09;这三个主要方面。这三个方面在前端开发中密切协作&#xff0c;共同构建用户界面和用户体验。 结构&#xff08;Structure&#xff09;&#xff…...

Flink-网络流控及反压剖析

参考&#xff1a; Apache Flink学习网...

redis 和 mongodb 比较

Redis和MongoDB是两种不同类型的数据库&#xff0c;它们在数据存储和查询方式、数据模型以及适用场景等方面有一些明显的区别。下面是Redis和MongoDB之间的一些比较&#xff1a; 数据模型&#xff1a; Redis&#xff1a;Redis是一个键值存储系统&#xff0c;支持多种数据结构如…...

Linux 主函数参数介绍

主函数如下&#xff1a; int main( int argc, char* argv[], char* envp[]) 参数分析如下&#xff1a; (1) argc 参数个数 (2) argv 参数内容&#xff0c;是char*类型&#xff0c;说明传给主函数的内容是一个一个的字符串。 (3) envp 环境变量&#xff0c;传给主函数的也…...

资料分析(三)—— 基期、现期、人口、增长量

基期 基期值 现期值 - 增长量 增长量/增长率 现期值/1&#xff08;间隔)增长率 化除为乘 &#xff1a;当增长率&#xff5c;r| < 5% 时&#xff0c;&#xff0c; 注&#xff1a;当选项首位相同&#xff0c;第二位也相同时&#xff0c;只能用直除 基期和差 (结合选…...

Java 正则表达式【匹配与分组基本原理】

简介 我们一般使用正则表达式是用来处理字符串的&#xff0c;不管是实际的开发中还是我们的算法竞赛中&#xff0c;使用正则表达式绝对可以大大提升我们的效率。 正则表达式&#xff08;regular expression&#xff09;其实就是对字符串进行模式匹配的技术。 快速入门 我们这里…...

ThreadLocal源码解析

使用ThreadLocal可以为每个线程维护一个线程变量&#xff0c;使用场景为线程间隔离&#xff0c;线程内方法共享&#xff1b; 原理&#xff1a; Thread类中有一个实例属性ThreadLocalMap&#xff0c;ThreadLocalMap中存放的是Entry数组&#xff0c;Entry数组是ThreadLocal和Ob…...

RocketMQ 5.1.0 源码详解 | Producer 发送流程

文章目录 初始化DefaultMQProducer实例发送流程DefaultMQProducer#sendDefaultMQProducerImpl#sendMQClientInstance#updateTopicRouteInfoFromNameServer使用特定 topic 获取路由信息使用默认 topic 获取路由信息 DefaultMQProducerImpl#sendDefaultImpl发送流程总结 初始化De…...

电脑ip地址怎么改 ip地址怎么改到别的城市

一、ip地址怎么改到别的城市 1.ip地址怎么改到别的城市&#xff0c;1、重启WIFI路由设备 一般手机或电脑在家或公司上网时都是接入到路由器的WIFI网络,再由路由器分配上网IP地址,如果要更换上网IP那么重启路由器设备后,路由器会向网络运营商进行宽带的重新拨号,此时手机或电脑设…...

Android Studio实现列表展示图片

效果&#xff1a; MainActivity 类 package com.example.tabulation;import android.content.Intent; import android.os.Bundle; import android.view.View;import androidx.appcompat.app.AppCompatActivity; import androidx.recyclerview.widget.LinearLayoutManager; im…...

每天一道leetcode:300. 最长递增子序列(动态规划中等)

今日份题目&#xff1a; 给你一个整数数组 nums &#xff0c;找到其中最长严格递增子序列的长度。 子序列 是由数组派生而来的序列&#xff0c;删除&#xff08;或不删除&#xff09;数组中的元素而不改变其余元素的顺序。例如&#xff0c;[3,6,2,7] 是数组 [0,3,1,6,2,2,7] …...

【无监督】2、MAE | 自监督模型提取的图像特征也很能打!(CVPR2022 Oral)

文章目录 一、背景二、方法三、效果 论文&#xff1a;Masked Autoencoders Are Scalable Vision Learners 代码&#xff1a;https://github.com/facebookresearch/mae 出处&#xff1a;CVPR2022 Oral | 何凯明 | FAIR 一、背景 本文的标题突出了两个词&#xff1a; masked…...

pytorch单机多卡后台运行

nohup sh ./train_chat.sh > train_chat20230814.log 2>1&参考资料 Pytorch单机多卡后台运行的解决办法...

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...

在四层代理中还原真实客户端ngx_stream_realip_module

一、模块原理与价值 PROXY Protocol 回溯 第三方负载均衡&#xff08;如 HAProxy、AWS NLB、阿里 SLB&#xff09;发起上游连接时&#xff0c;将真实客户端 IP/Port 写入 PROXY Protocol v1/v2 头。Stream 层接收到头部后&#xff0c;ngx_stream_realip_module 从中提取原始信息…...

PL0语法,分析器实现!

简介 PL/0 是一种简单的编程语言,通常用于教学编译原理。它的语法结构清晰,功能包括常量定义、变量声明、过程(子程序)定义以及基本的控制结构(如条件语句和循环语句)。 PL/0 语法规范 PL/0 是一种教学用的小型编程语言,由 Niklaus Wirth 设计,用于展示编译原理的核…...

深入解析C++中的extern关键字:跨文件共享变量与函数的终极指南

&#x1f680; C extern 关键字深度解析&#xff1a;跨文件编程的终极指南 &#x1f4c5; 更新时间&#xff1a;2025年6月5日 &#x1f3f7;️ 标签&#xff1a;C | extern关键字 | 多文件编程 | 链接与声明 | 现代C 文章目录 前言&#x1f525;一、extern 是什么&#xff1f;&…...

GC1808高性能24位立体声音频ADC芯片解析

1. 芯片概述 GC1808是一款24位立体声音频模数转换器&#xff08;ADC&#xff09;&#xff0c;支持8kHz~96kHz采样率&#xff0c;集成Δ-Σ调制器、数字抗混叠滤波器和高通滤波器&#xff0c;适用于高保真音频采集场景。 2. 核心特性 高精度&#xff1a;24位分辨率&#xff0c…...

Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信

文章目录 Linux C语言网络编程详细入门教程&#xff1a;如何一步步实现TCP服务端与客户端通信前言一、网络通信基础概念二、服务端与客户端的完整流程图解三、每一步的详细讲解和代码示例1. 创建Socket&#xff08;服务端和客户端都要&#xff09;2. 绑定本地地址和端口&#x…...

Python Einops库:深度学习中的张量操作革命

Einops&#xff08;爱因斯坦操作库&#xff09;就像给张量操作戴上了一副"语义眼镜"——让你用人类能理解的方式告诉计算机如何操作多维数组。这个基于爱因斯坦求和约定的库&#xff0c;用类似自然语言的表达式替代了晦涩的API调用&#xff0c;彻底改变了深度学习工程…...

[ACTF2020 新生赛]Include 1(php://filter伪协议)

题目 做法 启动靶机&#xff0c;点进去 点进去 查看URL&#xff0c;有 ?fileflag.php说明存在文件包含&#xff0c;原理是php://filter 协议 当它与包含函数结合时&#xff0c;php://filter流会被当作php文件执行。 用php://filter加编码&#xff0c;能让PHP把文件内容…...

学习一下用鸿蒙​​DevEco Studio HarmonyOS5实现百度地图

在鸿蒙&#xff08;HarmonyOS5&#xff09;中集成百度地图&#xff0c;可以通过以下步骤和技术方案实现。结合鸿蒙的分布式能力和百度地图的API&#xff0c;可以构建跨设备的定位、导航和地图展示功能。 ​​1. 鸿蒙环境准备​​ ​​开发工具​​&#xff1a;下载安装 ​​De…...

深度剖析 DeepSeek 开源模型部署与应用:策略、权衡与未来走向

在人工智能技术呈指数级发展的当下&#xff0c;大模型已然成为推动各行业变革的核心驱动力。DeepSeek 开源模型以其卓越的性能和灵活的开源特性&#xff0c;吸引了众多企业与开发者的目光。如何高效且合理地部署与运用 DeepSeek 模型&#xff0c;成为释放其巨大潜力的关键所在&…...