当前位置: 首页 > 工具软件 > WebAssembly > 使用案例 >

python wasm_webAssembly

方长卿
2023-12-01

What WebAssembly ?WebAssembly简称WASM,是一种以安全有效的方式运行便携式程序的新技术,主要针对Web平台。与ASM.js类似,WASM的目标是低级别的抽象,适合作为更高级别程序的中间表示 - 即WebAssembly代码旨在由编译器生成而不是由人类编写。在W3C社区组包括来自最大的网络浏览器的公司,包括谷歌,微软,苹果和Mozilla做这件事,而令人兴奋的代表。

WebAssembly是一种新的适合于编译到Web的,可移植的,大小和加载时间高效的格式,是一种新的字节码格式。它的缩写是”.wasm”,.wasm 为文件名后缀,是一种新的底层安全的“二进制”语法。它被定义为“精简、加载时间短的格式和执行模型”,并且被设计为Web 多编程语言目标文件格式。

这意味着浏览器端的性能会得到极大提升,它也使得我们能够实现一个底层构建模块的集合.

webAssembly的优势

webassembly相较于asm.js的优势主要是涉及到性能方面。根据WebAssembly FAQ的描述:在移动设备上,对于很大的代码库,asm.js仅仅解析就需要花费20-40秒,而实验显示WebAssembly的加载速度比asm.js快了20倍,这主要是因为相比解析 asm.js 代码,JavaScript 引擎破译二进制格式的速度要快得多。

主流的浏览器目前均支持webAssembly。

Safari 支持 WebAssembly的第一个版本是 11

Edge 支持 WebAssembly的第一个版本是 16

Firefox 支持 WebAssembly的第一个版本是 52

chrome 支持 WebAssembly的第一个版本是 57

使用WebAssembly,我们可以在浏览器中运行一些高性能、低级别的编程语言,可用它将大型的C和C++代码库比如游戏、物理引擎甚至是桌面应用程序导入Web平台。

开发前准备工作

操作系统MAC系统

安装工具git 、node 、python 、cmake、emsdk

git

已经默认装

node

python

cmake

为了可以在命令行中使用,需要执行一下命令。

sudo “/Applications/CMake.app/Contents/bin/cmake-gui” –install

emsdk

Emscripten 的底层是 LLVM 编译器,理论上任何可以生成 LLVM IR(Intermediate Representation)的语言,都可以编译生成 asm.js。 但是实际上,Emscripten 几乎只用于将 C / C++ 代码编译生成 asm.js。

** C/C++ ** ⇒ ** LLVM ** ==> ** LLVM IR ** ⇒ ** Emscripten ** ⇒ ** asm.js **

1

2

3

4

5

6

7

8

9

10git clone https://github.com/juj/emsdk.git

cd emsdk

./emsdk update

./emsdk install latest

./emsdk activate latest

source ./emsdk_env.sh

浏览器支持Chrome: 打开 chrome://flags/#enable-webassembly,选择 enable。

Firefox: 打开 about:config 将 javascript.options.wasm 设置为 true。

开始编程

编写C代码

创建** example.c ** 文件,并写入如下代码

1

2

3

4

5

6// example.c

float add(float a,float b){

return a+b;

}

编译出wasm

** bash 执行命令: **

1emcc -s EXPORTED_FUNCTIONS="['_add]" example.c -s WASM=1 -s SIDE_MODULE=1 -s BINARYEN_ASYNC_COMPILATION=0 -o example.wasm

调用wasmjs调用wasm中导出的module

** loader模块编写 **

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28function loadWebAssembly(path, imports = {}){

return fetch(path)

.then(response => response.arrayBuffer())

.then(buffer => WebAssembly.compile(buffer))

.then(module => {

//针对wasm所需的import处理,importObject

imports.env = imports.env || {}

imports.env = imports.env || {}

imports.env.DYNAMICTOP_PTR = imports.env.DYNAMICTOP_PTR || 0;

imports.env.tempDoublePtr = imports.env.tempDoublePtr || 0;

imports.env.ABORT = imports.env.ABORT || 0;

imports.global = imports.global || { NaN: 1, Infinity: 2 };

imports.env.abortStackOverflow = imports.env.abortStackOverflow || new Function();

imports.env.nullFunc_X = imports.env.nullFunc_X || new Function();

// 开辟内存空间

imports.env.memoryBase = imports.env.memoryBase || 0

if (!imports.env.memory) {

imports.env.memory = new WebAssembly.Memory({ initial: 256 })

}

// 创建变量映射表

imports.env.tableBase = imports.env.tableBase || 0

if (!imports.env.table) {

imports.env.table = new WebAssembly.Table({ initial: 2, element: 'anyfunc' })

}

// 创建 WebAssembly 实例

return new WebAssembly.instantiate(module, imports)

})

}

html片段1

2

3

4

5

6

7

8

9

10

11

12

13取随机数

const wasm = loadWebAssembly('example.wasm');

let myButton = document.getElementsByClassName('mybutton')[0];

const x = 1.22;

const y = 2.11;

myButton.onclick = function (){

wasm.then(module => {

console.log('wasm-计算:' + module.exports._add(x, y));

console.log('js-计算:' + (x + y));

});

}

WebAssembly组成部分实际上** WebAssembly **被称为 “模块”,因为使用WebAssembly,”程序”和”库”之间没有区别 - 只有”模块”,每个模块都可以与其他模块绑定和通信,每个模块都可以具有”模块”主功能。

必选部分Type:在模块中定义的函数的函数声明和所有引入函数的函数声明。

Function:给出模块中每个函数一个索引。

Code:模块中每个函数的实际函数体。

可选部分Export:使函数、内存、表(tables)、全局变量等对其他 WebAssembly 或 JavaScript 可见,允许动态链接一些分开编译的组件,即 .dll 的WebAssembly 版本。

Import:允许从其他 WebAssembly 或者 JavaScript 中导入指定的函数、内存、表或者全局变量。

Start:当 WebAssembly 模块加载进来的时候,可以自动运行的函数(类似于 main 函数)。

Global:声明模块的全局变量。

Memory:定义模块用到的内存。

Table:使得可以映射到 WebAssembly 模块以外的值,如映射到 JavaScript 的对象。这在间接函数调用时很有用。

Data:初始化导入的或者局部内存。

Element:初始化导入的或者局部的表。

遇到的坑

无法导入环境变量在执行 source ./emsdk_env.sh 命令时失败,无法添加环境变量

思路:脚本实现不了,就不要使用提供的脚本命令,手动添加也可以。最终是要在任何窗口中都能执行命令。

手动添加环境变量,一般都是添加在当前用户区,和windows理念一样。

1

2

3

4

5

6

7

8

9

10

11PATH="/Library/Frameworks/Python.framework/Versions/3.7/bin:${PATH}"

export PATH=/Users/liuzhipan/emsdk:$PATH

export PATH=/Users/liuzhipan/emsdk/clang/e1.38.8_64bit:$PATH

export PATH=/Users/liuzhipan/emsdk/node/8.9.1_64bit/bin:$PATH

export PATH=/Users/liuzhipan/emsdk/emscripten/1.38.8:$PATH

alias python='/Library/Frameworks/Python.framework/Versions/3.7/bin/python3.7'

export EMSDK=/Users/liuzhipan/emsdk

export EM_CONFIG=/Users/liuzhipan/.emscripten

export BINARYEN_ROOT=/Users/liuzhipan/emsdk/clang/e1.38.8_64bit/binaryen

export EMSCRIPTEN=/Users/liuzhipan/emsdk/emscripten/1.38.8

export PATH=$PATH:$EMSDK:$EM_CONFIG:$BINARYEN_ROOT:$EMSCRIPTEN

仿照这份环境变量修改对应目录-路径即可。

vim ~/.bash_profile //添加上述的环境变量

source ~/.bash_profile // 刷新当前用户环境变量缓冲,使其生效

实例化module失败构建出来包之后,发现importObject提供的资源缺少导致实例化module失败。

思路:官网提供的大部分都是env对象

1

2

3

4

5// 开辟内存空间

imports.env.memoryBase = imports.env.memoryBase || 0

if (!imports.env.memory) {

imports.env.memory = new WebAssembly.Memory({ initial: 256 })

}

1

2

3

4

5// 创建变量映射表

imports.env.tableBase = imports.env.tableBase || 0

if (!imports.env.table) {

imports.env.table = new WebAssembly.Table({ initial: 2, element: 'anyfunc' })

}

** 问题关键 **

可是我导出来的wasm-module不止这几个。

1

2

3

4

5

6

7

8

9

10

11

12(import "env" "memory" (memory $env.memory 256))

(import "env" "table" (table $env.table 2 anyfunc))

(import "env" "memoryBase" (global $env.memoryBase i32))

(import "env" "tableBase" (global $env.tableBase i32))

(import "env" "DYNAMICTOP_PTR" (global $env.DYNAMICTOP_PTR i32))

(import "env" "tempDoublePtr" (global $env.tempDoublePtr i32))

(import "env" "ABORT" (global $env.ABORT i32))

(import "global" "NaN" (global $global.NaN f64))

(import "global" "Infinity" (global $global.Infinity f64))

(import "env" "abortStackOverflow" (func $env.abortStackOverflow (type $t0)))

(import "env" "nullFunc_X" (func $env.nullFunc_X (type $t0)))

(func $f2 (type $t1) (param $p0 i32) (result i32)

** 处理方案: **

所以要对多导出来的资源进行处理。

这些都是实例化module需要提供的import.加入对应的字段即可处理!

1

2

3

4

5

6imports.env.DYNAMICTOP_PTR = imports.env.DYNAMICTOP_PTR || 0;

imports.env.tempDoublePtr = imports.env.tempDoublePtr || 0;

imports.env.ABORT = imports.env.ABORT || 0;

imports.global = imports.global || { NaN: 1, Infinity: 2 };

imports.env.abortStackOverflow = imports.env.abortStackOverflow || new Function();

imports.env.nullFunc_X = imports.env.nullFunc_X || new Function();

还有一种更简洁的。

使用wasm-gc去处理。

 类似资料:

相关阅读

相关文章

相关问答