掘金 后端 ( ) • 2024-04-17 17:41

theme: nico highlight: atelier-dune-dark

使用 WebAssembly 在前端项目中解决问题

简介

在前端项目中,使用 JavaScript 进行加减乘除运算时,可能会遇到精度问题。这是因为 JavaScript 使用浮点数来表示数字,而浮点数在某些情况下会出现精度丢失。

为了解决这个问题,可以使用 JavaScript 和 WebAssembly 结合的方式来实现原生模块,以获得更高的计算精度和性能。

步骤

如果还没安装本地环境的或者对c++语法还不熟悉的朋友可以稍微看一下我的之前写的 c++入门指南包含了入门语法和本地环境的安装,好了废话不多说本文比较简单,核心在于突出降维打击的能力,(每个人的理解不一样,别跟我抬杠我这里只是举个简单的例子,不必ETC!)

1. 编写 C++ 代码

首先,编写包含加法、减法、乘法和除法等功能的 C++ 代码。以下是一个简单的示例:

// math.cpp
extern "C" {
  double add(double a, double b) { return a + b; }
  double subtract(double a, double b) { return a - b; }
  double multiply(double a, double b) { return a * b; }
  double divide(double a, double b) {
    if (b != 0) {
      return a / b;
    } else {
      return NAN; // 返回 NaN 表示除数为零的情况
    }
  }
}

2. 编译成 WebAssembly 模块

将上述 C++ 代码编译成可在浏览器中运行的 WebAssembly 模块。你可以使用 Emscripten 工具链或其他类似的工具。

具体的编译命令会因工具链而异,以下是使用 Emscripten 的基本步骤:

1. 安装 Emscripten:安装 Emscripten 工具链所需要执行一系列步骤:

  • 1.1.安装依赖项:在安装 Emscripten 之前,需要确保系统中已安装了一些依赖项,如 CMake、Node.js 和 Python。具体依赖项可能因操作系统而异,可以在 Emscripten 官方文档中找到详细信息。

  • 1.2. 获取 Emscripten 安装脚本:Emscripten 提供了一个安装脚本,可用于下载和配置工具链。可以通过以下命令获取安装脚本:

    git clone https://github.com/emscripten-core/emsdk.git
    
  • 1.3. 运行安装脚本:进入下载的 emsdk 目录,并运行安装脚本:

    cd emsdk
    ./emsdk install latest
    

    这将下载并安装最新版本的 Emscripten 工具链。

  • 1.4. 激活 Emscripten 环境:安装完成后,需要激活 Emscripten 环境,使得系统可以使用 Emscripten 工具链。在 emsdk 目录下运行以下命令:

    ./emsdk activate latest
    

  • 1.5. 设置环境变量:执行完上面的安装完成后,可能需要将 Emscripten 工具链的路径添加到系统的环境变量中,这样可以在任何位置使用 emcc 和其他 Emscripten 工具。可以通过编辑 .bashrc.bash_profile.zshrc 等文件来设置环境变量,将 Emscripten 相关的路径添加到 PATH 变量中。

  • 1.6. 验证安装:安装完成后,可以通过运行以下命令来验证 Emscripten 是否安装成功:

    emcc --version
    

    如果成功安装,将显示 Emscripten 工具链的版本信息。

安装完成后,就可以在系统中使用 emcc 和其他 Emscripten 工具了。请注意,安装过程可能因操作系统、网络连接等因素而略有不同,建议参考 Emscripten 官方文档以获取最新的安装指南和细节。 确保已经安装了 Emscripten 工具链,并且 emcc 和其他 Emscripten 工具的可执行文件在系统路径中。

2. 编译代码:使用以下命令将 C++ 代码编译成 WebAssembly 模块:

emcc math.cpp -s WASM=1 -s MAIN_MODULE=1 -o math.wasm

说明: 这条命令是使用 Emscripten 编译器 (emcc) 将 C++ 文件 math.cpp 编译成 WebAssembly 文件 math.wasm 的命令。下面是各个部分的含义:

emcc: 这是 Emscripten 编译器的命令行工具。(上面我们安装完成的工具)

math.cpp: 这是要编译的源代码文件,它包含了 C++ 代码。

-s WASM=1: 这个选项告诉编译器将输出文件编译为 WebAssembly 格式。-s 是 Emscripten 的编译器选项的前缀,WASM=1 是其中一个选项,表示生成的文件是 WebAssembly 文件。

-s MAIN_MODULE=1: 这个选项告诉编译器生成一个包含 Emscripten 运行时的主模块(main module)。主模块是一个包含了整个 Emscripten 运行时的模块,它提供了 WebAssembly 文件运行所需的所有功能。这个选项通常用于生成一个独立的、可执行的 WebAssembly 文件。

-o math.wasm: 这个选项指定了编译器生成的输出文件的名称,即 math.wasm。.wasm 文件是 WebAssembly 格式的二进制文件,包含了编译后的程序代码。

这条命令使用 Emscripten 编译器将 math.cpp 编译为 WebAssembly 格式,并生成一个包含 Emscripten 运行时的主模块,最终输出到 math.wasm 文件中。

3. 在前端项目中调用 WebAssembly 模块

使用 JavaScript 在前端项目中加载和调用编译生成的 WebAssembly 模块。以下是基本的调用示例:

// main.js
// 异步加载 WebAssembly 模块
const wasmModule = fetch('math.wasm')
  .then(response => response.arrayBuffer())
  .then(bytes => WebAssembly.instantiate(bytes, {}))
  .then(obj => obj.instance);
​
// 调用加法函数
wasmModule.then(instance => {
  const addResult = instance.exports.add(5, 3);
  console.log('5 + 3 =', addResult);
});
​
// 调用减法函数
wasmModule.then(instance => {
  const subtractResult = instance.exports.subtract(5, 3);
  console.log('5 - 3 =', subtractResult);
});
​
// 调用乘法函数
wasmModule.then(instance => {
  const multiplyResult = instance.exports.multiply(5, 3);
  console.log('5 * 3 =', multiplyResult);
});
​
// 调用除法函数
wasmModule.then(instance => {
  const divideResult = instance.exports.divide(5, 3);
  console.log('5 / 3 =', divideResult);
});

注意事项

  • 在编译 C++ 代码时,需要使用 -s WASM=1 参数启用 WebAssembly 支持。
  • 在加载 WebAssembly 模块时,需要使用 WebAssembly.instantiate 函数。
  • 在调用 WebAssembly 模块的导出函数时,需要使用 instance.exports 对象。

在实际项目中的考虑

在实际项目中,除了基本的编译和调用步骤外,还需要考虑异常处理、性能优化等方面的问题。同时,对于涉及财务计算等对精度要求较高的场景,需要进行更加严格的测试和验证,以确保计算结果的准确性和可靠性。

总结

通过使用 WebAssembly,可以在前端项目中解决 JavaScript 计算精度丢失的问题,提高计算精度和性能。这为前端开发人员提供了一种新的选择,可以更好地满足复杂计算需求,并改善用户体验。

其实本文只是牛刀小试,三年前我们也曾使用WebAssembly技术的方式实现前端播放FLV格式视频以及H256的视频直播内容并且实现16分屏的实战项目。感兴趣的朋友可以去我们的github开源项目magic-videoplayer去看看吧。如果你也对视频编解码以及播放器内容感兴趣的话