WebAssembly实践入门

1,955 阅读2分钟

WebAssembly(或Wasm)在今天已经并不陌生了,它设计的主要目标是在Web上运行高性能程序,弥补JavaScript的不足。这篇文章介绍如何将一个简单的C文件编译为wasm模块,并在Webpack中使用。

环境安装

C/C++的编译工具为Emscripten,可以参考MDN上的这篇文章安装。

编译wasm模块

这里以只有一个add函数的C文件做简单示例:

#include <emscripten.h>

EMSCRIPTEN_KEEPALIVE
int add(int a, int b)
{
    return a + b;
}

EMSCRIPTEN_KEEPALIVE用来标记需要导出的函数。

接下来我们运行emcc add.c -o add.js,它将编译生成 add.wasmadd.js 两个文件。add.js 中包含了如何引入wasm文件并运行的一套代码,它是可以直接在html中引入或者在node中运行的。我们暂时不管这个JS文件,来看怎么使用生成的wasm模块。

在JS中引入wasm模块

在wasm文件目录下创建一个html文件,内容如下:

<html lang="en">
<head>
    <title>Wasm Example</title>
</head>
<body>
    <script>
        async function run() {
            const fetchPromise = fetch('add.wasm');
            const { instance } = await WebAssembly.instantiateStreaming(fetchPromise);
            console.log(instance.exports.add(1, 2))
        }

        run()
    </script>
</body>

在浏览器中运行即可看到控制台中打印内容3,这说明我们的wasm模块已经加载并运行成功了。

const fetchPromise = fetch("add.wasm"); // 加载wasm文件
const { instance } = await WebAssembly.instantiateStreaming(fetchPromise); // 编译同时实例化
console.log(instance.exports.add(1, 2)); // 所有导出的函数都在 instance.exports 对象中

关于如何高效加载wasm模块可以参考Web Dev的这篇文章。使用wasm需要经过下载,编译,实例化3个步骤,在编写代码时需要注意使用异步API避免阻塞主进程以及使用streaming接口进行优化。

在Webpack中使用wasm模块

Webpack5中新增了asyncWebAssemblysyncWebAssembly两个实验属性用于支持wasm模块。

我们参考官网搭建一个简单的webpack项目,安装webpack与webpack-cli,并添加webpack.config.js文件如下:

const path = require("path");

module.exports = {
  mode: "development",
  entry: "./src/index.js",
  output: {
    filename: "main.js",
    path: path.resolve(__dirname, "dist"),
  },
  devtool: false,
  experiments: {
    asyncWebAssembly: true,
    syncWebAssembly: true,
  },
};

现在我们即可通过下面这样简单的方式来使用wasm模块了:

import { add } from "./wasm/add.wasm";

console.log("add", add(100, 1));

查看webpack build后的结果,会发现除了生成 main.js,还会有一个wasm文件,这个文件即是之前的 add.wasm

截屏2023-02-20 12.56.17.png

在执行wasm的方法前,webpack会先检测wasm模块是否加载和初始化完成,与我们之前的方法相似。

image.png

More

到这里,我们已经可以编译C文件为wasm,并知道如何在JS或Webpack中使用了。

其实Emscripten还有很多非常有用的配置,如emcc add.c -s ENVIRONMENT=web,worker可以指定运行环境,emcc add.c -pthread可以生成worker文件,让wasm的代码在web worker中运行。有兴趣的同学可以继续探索~

参考