WebAssembly 入门教程 c++、python编译wasm

WebAssembly 入门

了解 wasm 使用场景,复杂对象传递和经验法则。

简介

WebAssembly 是一种新的编码方式,可以在现代的网络浏览器中运行。它是一种低级的类汇编语言,具有紧凑的二进制格式,可以接近原生的性能运行,并为诸如 C/C ++ 等语言提供一个编译目标,以便它们可以在 Web 上运行。它也被设计为可以与 JavaScript 共存,允许两者一起工作。

WebAssembly 提供了一条途径,使得以各种语言编写的代码都可以以接近原生的速度在 Web 中运行。

WebAssembly 设计初衷

  • 它设计的目的不是为了手写代码而是为了诸如 C、C++ 和 Rust 等低级源语言提供一个高效的编译目标。

  • WebAssembly 的模块可以被导入的到一个网络 app(或 Node.js)中,并且暴露出供 JavaScript 使用的 WebAssembly 函数。JavaScript 框架不但可以使用 WebAssembly 获得巨大性能优势和新特性,而且还能使得各种功能保持对网络开发者的易用性。

如何得到 WebAssembly 二进制文件

  • 现代语言几乎都支持将 wasm 作为它的编译输出,如 Go、Python、C/C++、Rust、TypeScript 等都可以,只是由于 wasm 因为需要通过网络传播,因此大小很重要,因此更推荐如 C/C++、Rust 没有像垃圾收集器那样额外的运行时语言,可以使 wasm 体积更小。

  • 直接编写 wasm 代码(了解即可)

    • wasm 的二进制格式也有文本表示,两者之间 1:1 对应。你可以手工书写或者生成这种格式然后使用工具把它转换为二进制格式。这是一种用来在文本编辑器、浏览器开发者工具等工具中显示的中间形式

    • 二进制格式通常为 .wasm 格式,文本格式通常为 .wat 格式

    • 理解 WebAssembly 文本格式

WebAssembly 优势

  • 紧凑的二进制格式,使其能够以接近原生性能的速度运行,并支持在各种上下文中使用

  • 为诸如 C++ 和 Rust 等拥有低级的内存模型语言提供了一个编译目标以便它们能够在网络上运行

WebAssembly 劣势

  • 对于编写网络应用程序而言,不如 JavaScript 灵活且富有表达力

  • 只有很小的一个值类型集合,基本上限制在简单数值的范围,复杂数据类型需要进行编解码,如字符串、对象、数组需要先编码成二进制再存放到 wasm 内存段里

  • 与 JavaScript 胶水代码的交互带来的性能损耗一定程度上抵消了 wasm 本身带来的性能提升

使用场景

WebAssembly = NaCl + asm.js

随着技术的发展,Mozilla 和 Google 的工程师出现了很多次的交流和合作,通过汲取 NaCl 和 asm.js 两者的优点,双方推出了一种全新的技术方案:

  • 和 NaCl/PNaCl 一样,基于二进制格式,从而能够被快速解析,达到原生代码的运行速度;

  • 和 PNaCl 一样,依赖于通用的 LLVM IR,这样既具备可移植性,又便于其他语言快速接入;

  • 和 asm.js 一样,使用 Emscripten 等工具链进行编译;另外,Emscripten 同时支持生成 asm.js 和二进制格式,当浏览器不兼容新的二进制格式时,asm.js 可以作为降级方案;

  • 和 asm.js 一样,必须以非常自然的方式直接操作 Web API,而不用像 PNaCl 一样需要处理与 JavaScript 之间的通信;

这个技术方案在 2015 年正式命名为 WebAssembly,2017 年各大浏览器生产商纷纷宣布支持 WebAssembly,2019 年 WebAssembly 正式成为 W3C 标准,一场关于浏览器的性能革命已经悄然展开。

wasm-performance.png

使用 WebAssembly 的原因

  • 关注性能敏感代码:使用 Rust 你不需要成为 JS 优化专家,不需要熟悉 JIT 内部实现,不需要魔法也能加速。

  • 集成方便:直接编译为 .wasm,使得现有的 JS 代码库可以增量式部分采用 WebAssembly。而且还可以保持你现有代码库,不需要重写。

  • 复用已有的其他语言编写的代码模块

开发软件时使用 wasm 的常见方式

  • 纯 wasm 实现,包括 ui 和逻辑

  • UI 使用 HTML/CSS/JS,逻辑计算使用 wasm

  • 复用其他语言的库,使用 wasm 移植到已有的 web 软件中

现有的使用 wasm 编写的应用有

  • Google Earth

  • AutoCAD Web

  • PhotoShop Web:Web 端和 PC 端由一份编码编译生成

  • Figma:wasm+rust 的 web 应用框架 zaplib

  • bilibili:wasm 版的 FFmpeg/tensorflow

WebAssembly 入门示例

从上面的学习中我们知道,WebAssembly 是一种通用的编码格式,并且已经有很多编程语言支持将源码编译成这种格式了,官方的 Getting Started 有一个详细的列表。这一节我们就跟着官方的教程实践一下下面这三种语言:

  • C/C++

  • Rust

  • Go

将 C/C++ 程序编译成 WebAssembly

配置环境

前提条件

需要安装CMake,VS,python2.7及以上

拉取emsdk代码

# Get the emsdk repo
git clone https://github.com/emscripten-core/emsdk.git
​
# Enter that directory
cd emsdk

安装并激活Emscripten

emsdk.bat install latest
​
emsdk.bat activate latest
​
在cmd窗口运行如上,有时需要./

设置环境变量

emsdk_evn.bat

要注意的是:每次执行emcc前都要执行改命令,这是因为这个环境变量设置并不是全局的,如果使用emsdk.bat activate latest --global可以将命令更改为系统的环境变量,这样以后就不用再做环境变量的设置,但是他指向了Emscripten内置的Node.js,Python,java,如果系统中还有其他版本就可能产生冲突

安装完成后通过下面的命令检查环境是否正常:

emcc --check
emcc (Emscripten gcc/clang-like replacement + linker emulating GNU ld) 3.1.59 (0e4c5994eb5b8defd38367a416d0703fd506ad81)
shared:INFO: (Emscripten: Running sanity checks)

环境准备就绪后,我们就可以将 C/C++ 的代码编译为 WebAssembly 了。写一个简单的 Hello World 程序 hello.c

#include <stdio.h>int main() {printf("Hello World\n");return 0;
}

然后使用 emcc 进行编译:

emcc hello.c -o hello.html

上面这个命令会生成三个文件:

  • hello.wasm - 这就是生成的 WebAssembly 二进制字节码文件

  • hello.js - 包含一段胶水代码(glue code)通过 JavaScript 来调用 WebAssembly 文件

  • hello.html - 方便开发调试,在页面上显示 WebAssembly 的调用结果

我们不能直接用浏览器打开 hello.html 文件,因为浏览器不支持 file:// 形式的 XHR 请求,所以在 HTML 中无法加载 .wasm 等相关的文件,为了看到效果,我们需要一个 Web Server,比如 Nginx、Tomcat 等,不过这些安装和配置都比较麻烦,我们还有很多其他的方法快速启动一个 Web Server。

比如通过 npm 启动一个本地 Web Server:

$ npx serve .

或者使用 Python3 的 http.server 模块:

$ python3 -m http.server

访问 hello.html 页面如下:

可以看到我们在 C 语言中打印的 Hello World 成功输出到浏览器了。

另外,我们也可以将 C 语言中的函数暴露出来给 JavaScript 调用。默认情况下,Emscripten 生成的代码只会调用 main() 函数,其他函数忽略。我们可以使用 emscripten.h 中的 EMSCRIPTEN_KEEPALIVE 来暴露函数,新建一个 greet.c 文件如下:

#include <stdio.h>
#include <emscripten/emscripten.h>
​
int main() {printf("Hello World\n");return 0;
}
​
#ifdef __cplusplus
#define EXTERN extern "C"
#else
#define EXTERN
#endif
​
EXTERN EMSCRIPTEN_KEEPALIVE void greet(char* name) {printf("Hello, %s!\n", name);
}
​
// Fibonacci function
EXTERN int EMSCRIPTEN_KEEPALIVE fibonacci(int n) {if (n <= 1) {return n;} else {return fibonacci(n - 1) + fibonacci(n - 2);}
}
​

上面的代码定义了一个 void greet(char* name) 函数,为了让这个函数可以在 JavaScript 中调用,编译时还需要指定 NO_EXIT_RUNTIMEEXPORTED_RUNTIME_METHODS 参数,将 ccall 导出来:

emcc -o greet.html greet.c -s NO_EXIT_RUNTIME=1 -s EXPORTED_RUNTIME_METHODS=ccall

greet.html 文件和上面的 hello.html 几乎是一样的,我们在该文件中加几行代码来测试我们的 greet() 函数,首先加一个按钮:

<input type="text" id="myinput" value=10>
<button id="mybutton">Click me!</button>

然后为它添加点击事件,可以看到 JavaScript 就是通过上面导出的 ccall 来调用 greet() 函数的:

document.getElementById("mybutton").addEventListener("click", () => {const result = Module.ccall("greet",         // name of C functionnull,            // return type["string"],      // argument types["WebAssembly"]  // arguments);const n = parseInt(document.getElementById("myinput").value, 10);const result2 = Module.ccall("fibonacci", // name of C function"number",    // return type["number"],  // argument types[n]          // arguments);console.log("The Fibonacci number is: " + result2);});

除了 ccall,我们还可以使用 -s EXPORTED_RUNTIME_METHODS=ccall,cwrap 同时导出 ccallcwrap 函数。ccall 的作用是直接调用某个 C 函数,而 cwrap 是将 C 函数编译为一个 JavaScript 函数,并可以反复调用,这在正式项目中更实用。

点击这个按钮,可以在页面和控制台上都看到 greet() 函数打印的内容:

输入的数字太大,直接爆了...

Uncaught RuntimeError: memory access out of bounds

其他导入方式

在Nodejs中使用WebAssembly

比如我们有如下c代码 testWebAssembly.c

#include <stdio.h>
int output()
{return 1;
}
int add(int a, int b)
{return a + b;
}
emcc testWebAssembly.c -Os -s WASM=1 -s SIDE_MODULE=1 -o testWebAssembly.wasm

上面的编译选项-s WASM=1是告诉编译器将代码编译成一个 WebAssembly 模块(即侧向模块),而不是一个完整的可执行程序。这意味着该程序不包含 main 函数,也不包含任何与操作系统或文件系统相关的代码。这使得该程序可以被其他程序通过 WebAssembly API 调用,并且可以在各种不同的环境中运行,例如浏览器、Node.js 等。

我一开始编译确实-s SIDE_MODULE=1,导致编译出来的testWebAssembly.wasm在实际使用的时候一直报错如下,加了该选项之后就正常

WebAssembly.Instance(): Import #0 module="wasi_snapshot_preview1" error: module is not an object or function 

-s WASM=1是 emsdk / emscripten 中的一个编译选项,它告诉 emscripten 编译器将输出编译为 WebAssembly 格式。具体来说,它启用了 Emscripten 的 WebAssembly 后端,该后端将 C/C++代码编译为 WebAssembly 模块。

在使用 Emscripten 编译 C/C++代码时,如果希望将其编译为 WebAssembly 格式,必须使用 -s WASM=1选项。如果您没有使用该选项,编译器将默认为 asm.js 格式进行编译。

WebAssembly 是一种新型的低级字节码格式,可以在浏览器中运行,能够以可移植的方式在不同的平台上执行代码。相比于 asm.js,WebAssembly 具有更快的解析速度和更小的体积,这使得它成为用于在浏览器中运行原生代码的最佳选择之一。

通过以上方式我们最终得到一个testWebAssembly.wasm

在Nodejs中使用WebAssembly

const fs = require('fs');const wasmCode = fs.readFileSync('./testWebAssembly.wasm');// 编译WebAssembly二进制代码const wasmModule = new WebAssembly.Module(toUint8Array(wasmCode));// 实例化WebAssembly模块const wasmInstance = new WebAssembly.Instance(wasmModule, {});const lib = wasmInstance.exports;
​// `Wasm` does **not** understand node buffers, but thankfully a node buffer// is easy to convert to a native Uint8Array.function toUint8Array(buf) {const u = new Uint8Array(buf.length);for (let i = 0; i < buf.length; ++i) {u[i] = buf[i];}return u;}console.log(lib.add(5, 5));console.log(lib.output());

最终我们就能成功在nodejs环境里直接调用c/c++代码的方法,使用方式比较简单,直接参看注释。

Python 编译成 WebAssembly

python转化成wasm,大了太多了

 
py2wasm pystone.py -o pystone.wasm

参考资料

Emscripten 的官方文档 Download and install — Emscripten 3.1.60-git (dev) documentation

WebAssembly 概念

WebAssembly 概念 - WebAssembly | MDN (mozilla.org)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/319382.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

c#学习基础1

一、复杂数据类型 1&#xff09;概述 2&#xff09;枚举 1.基本概念 枚举是一个比较特别的存在&#xff0c;它是一个被命名的整形常量的集合&#xff0c;一般用它来表示状态&#xff0c;类型等 1.1申明枚举和申明枚举变量 1.2申明枚举语法 2.在哪里申明枚举 3.枚举的使用 4…

【网络编程下】五种网络IO模型

目录 前言 一.I/O基本概念 1.同步和异步 2.阻塞和非阻塞 二.五种网络I/O模型 1.阻塞I/O模型 2.非阻塞式I/O模型 ​编辑 3.多路复用 4.信号驱动式I/O模型 5. 异步I/O模型 三.五种I/O模型比较​编辑 六.I/O代码示例 1. 阻塞IO 2.非阻塞I/O 3.多路复用 (1)select …

Llama 3 安装使用方法

Llama3简介&#xff1a; llama3是一种自回归语言模型&#xff0c;采用了transformer架构&#xff0c;目前开源了8b和70b参数的预训练和指令微调模型&#xff0c;400b正在训练中&#xff0c;性能非常强悍&#xff0c;并且在15万亿个标记的公开数据进行了预训练&#xff0c;比ll…

Leetcode—422. 有效的单词方块【简单】Plus

2024每日刷题&#xff08;126&#xff09; Leetcode—422. 有效的单词方块 实现代码 class Solution { public:bool validWordSquare(vector<string>& words) {int row words.size();for(int i 0; i < row; i) {// 当前这一行的列数int col words[i].length(…

Postman的一些使用技巧

Postman 是一个流行的 API 开发工具&#xff0c;用于设计、开发、测试、发布和监控 API。在现代web开发中使用非常广泛。后端开发必备而且必会的工具。 目录 1.配置环境变量 2.动态变量 3.脚本 4.测试 5.模拟 6.监控 7.集合运行器 8.响应保存 9.请求历史 10.同步请求…

如何在iPhone上恢复出厂设置后恢复数据

你不想让这种情况发生&#xff0c;但它确实发生了。您必须将iPhone恢复出厂设置。当您的 iPhone 上出现软件问题且无法修复时&#xff0c;可能会发生这种情况。相反&#xff0c;在更新期间&#xff0c;或者您的iPhone遇到问题时&#xff0c;iPhone上的数据不再存在。 不过不用…

leetcode_47.全排列 II

47. 全排列 II 题目描述&#xff1a;给定一个可包含重复数字的序列 nums &#xff0c;按任意顺序 返回所有不重复的全排列。 示例 1&#xff1a; 输入&#xff1a;nums [1,1,2] 输出&#xff1a; [[1,1,2],[1,2,1],[2,1,1]]示例 2&#xff1a; 输入&#xff1a;nums [1,2,3] …

性能优化(一):ArrayList还是LinkedList?

引言 集合作为一种存储数据的容器&#xff0c;是我们日常开发中使用最频繁的对象类型之一。JDK为开发者提供了一系列的集合类型&#xff0c;这些集合类型使用不同的数据结构来实现。因此&#xff0c;不同的集合类型&#xff0c;使用场景也不同。 很多同学在面试的时候&#x…

Flutter笔记:使用Flutter私有类涉及的授权协议问题

Flutter笔记 使用Flutter私有类涉及的授权协议问题 - 文章信息 - Author: 李俊才 (jcLee95) Visit me at CSDN: https://jclee95.blog.csdn.netMy WebSite&#xff1a;http://thispage.tech/Email: 291148484163.com. Shenzhen ChinaAddress of this article:https://blog.cs…

使用docker-compose编排lnmp(dockerfile)完成wordpress

文章目录 使用docker-compose编排lnmp&#xff08;dockerfile&#xff09;完成wordpress1、服务器环境2、Docker、Docker-Compose环境安装2.1 安装Docker环境2.2 安装Docker-Compose 3、nginx3.1 新建目录&#xff0c;上传安装包3.2 编辑Dockerfile脚本3.3 准备nginx.conf配置文…

【哈希表】Leetcode 14. 最长公共前缀

题目讲解 14. 最长公共前缀 算法讲解 我们使用当前第一个字符串中的与后面的字符串作比较&#xff0c;如果第一个字符串中的字符没有出现在后面的字符串中&#xff0c;我们就直接返回&#xff1b;反之当容器中的所有字符串都遍历完成&#xff0c;说明所有的字符串都在该位置…

Java枚举(enum)

“今天我们来学习枚举吧&#xff0c;三妹&#xff01;”我说&#xff0c;“同学让你去她家玩了两天&#xff0c;感觉怎么样呀&#xff1f;” “心情放松了不少。”三妹说&#xff0c;“可以开始学 Java 了&#xff0c;二哥。” “OK。” “枚举&#xff08;enum&#xff09;…

Linux服务器安全基础 - 查看入侵痕迹

1. 常见系统日志 /var/log/cron 记录了系统定时任务相关的日志 /var/log/dmesg 记录了系统在开机时内核自检的信息&#xff0c;也可以使用dmesg命令直接查看内核自检信息 /var/log/secure:记录登录系统存取数据的文件;例如:pop3,ssh,telnet,ftp等都会记录在此. /var/log/btmp:记…

【数据结构】链表专题2

前言 本篇博客继续探讨有关链表的专题&#xff0c;这片博客的题&#xff0c;提前打个预防针&#xff0c;有点意思哦&#xff0c;哈哈哈&#xff0c;话不多说&#xff0c;进入正文 &#x1f493; 个人主页&#xff1a;小张同学zkf ⏩ 文章专栏&#xff1a;数据结构 若有问题 评论…

ubuntu下安装配置python3.11

方案1 添加仓库&#xff1a; $ sudo add-apt-repository ppa:deadsnakes/ppa $ sudo apt update $ sudo apt install python3.11然后查看有多少个python版本已经安装了&#xff1a; ls -l /usr/bin/python*python2.7,python 3.8 ,python 3.11. 然后&#xff0c;设置系统默认…

【IC设计】CRC(循环冗余校验)

目录 理论解读CRC应用CRC算法参数解读常见CRC参数模型 设计实战校招编程题分类串行输入、并行计算、串行输出**串行计算、串行输出&#xff08;线性移位寄存器&#xff09;LSFR线性移位寄存器&#xff08;并转串&#xff09;(并行计算)模二除 总结——串行、并行计算的本质参考…

【数据结构】--- 深入剖析二叉树(上篇)--- 初识树和二叉树

Welcome to 9ilks Code World (๑•́ ₃ •̀๑) 个人主页: 9ilk (๑•́ ₃ •̀๑) 文章专栏&#xff1a; 数据结构之旅 &#x1f3e0; 初识树 &#x1f4d2; 树的概念 树是一种非线性的数据结构&#xff0c;它是由n&#xff08;n>0&#xff09;个有限结点…

旅游系列之:庐山美景

旅游系列之&#xff1a;庐山美景 一、路线二、住宿二、庐山美景 一、路线 庐山北门乘坐大巴上山&#xff0c;住在上山的酒店东线大巴游览三叠泉&#xff0c;不需要乘坐缆车&#xff0c;步行上下三叠泉即可&#xff0c;线路很短 二、住宿 长江宾馆庐山分部 二、庐山美景

SpringBoot 快速开始 Dubbo RPC

文章目录 SpringBoot 快速开始 Dubbo RPC下载 Nacos项目启动项目的创建创建主项目接口定义服务的创建Dubbo 服务提供者的创建服务的消费者创建 添加依赖给 Provider、Consumer 添加依赖 开始写代码定义接口在 Provider 中实现在 Consumer 里面使用创建启动类 注册中心配置启动 …

基于Spring Boot的校园博客系统设计与实现

基于Spring Boot的校园博客系统设计与实现 开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/idea 系统部分展示 系统功能界面图&#xff0c;在系统首页可以查看首页、文…