体验WebAssembly

3,221 阅读3分钟

原文地址: github.com/yinxin630/b…
技术交流: fiora.suisuijiang.com/

WebAssembly是什么

WebAssembly是一种运行在现代网络浏览器中的新型代码并且提供新的性能特性和效果。它设计的目的不是为了手写代码而是为诸如C、C++和Rust等低级源语言提供一个高效的编译目标。

对于网络平台而言,这具有巨大的意义——这为客户端app提供了一种在网络平台以接近本地速度的方式运行多种语言编写的代码的方式;在这之前,客户端app是不可能做到的。

而且,你可以在不知道如何编写WebAssembly代码的情况下就可以使用它。WebAssembly的模块可以被导入的到一个网络app(或Node.js)中,并且暴露出供JavaScript使用的WebAssembly函数。JavaScript框架不但可以使用WebAssembly获得巨大性能优势和新特性,而且还能使得各种功能保持对网络开发者的易用性。''

简言之, WebAssembly使得C/C++的代码可以在浏览器中运行

有什么好处呢? 一是 C/C++ 代码的执行效率优于 JavaScript, 可以提升 Web 应用的性能表现. 二是有机会将现有的 C/C++ 应用迁移到 Web 平台, 享受 Web 易传播的红利

环境安装 (MacOS)

要编译 C/C++ 到 wasm 需要安装 Emscripten 工具, 为了安装 Emscripten 你需要具备如下环境:

  • Git
  • Cmake
  • Xcode
  • Python 2.7.x

上述环境准备完毕后, 我们要编译 Emscripten, 按序执行如下命令:

git clone https://github.com/juj/emsdk.git
cd emsdk
./emsdk install --build=Release sdk-incoming-64bit binaryen-master-64bit
./emsdk activate --global --build=Release sdk-incoming-64bit binaryen-master-64bit

编译过程中会安装 Node.js, 即便你已经具备 Node.js 环境了, 如果在下载 Node.js 时报了网络错误, 请科学上网后再试

其他系统安装请参考: developer.mozilla.org/zh-CN/docs/…

生成 WebAssembly

创建 hello.c 文件, 复制如下代码

#include <stdio.h>

int main(int argc, char ** argv) {
    printf("Hello World\n");
}

然后将 c 源码文件编译为 js 文件, Emscripten 并不会自动添加到 PATH 中, 你需要在安装目录中执行如下命令

emcc hello.c -s WASM=1 -o hello.js

创建 hello.html 文件, 加载该 js 文件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Web Assembly</title>
</head>
<body>
    <script src="./hello.js"></script>
</body>
</html>

你需要通过 HTTP 协议来访问这个页面 可以用 nginx 或者代理软件, 还可以安装 http-server 这个 npm 包, 执行 npm install http-server -g, 运行服务 http-server

打开页面, 查看 console, 会输出 Hello World

将 Emscripten 添加到环境变量

emcc 位于 [安装目录]/emsdk/emscripten/incoming/

在 JavaScript 中调用 C 定义的函数

创建 callc.c 文件, 复制如下代码

#include <stdio.h>
#include <emscripten/emscripten.h>

#ifdef __cplusplus
extern "C" {
#endif

int EMSCRIPTEN_KEEPALIVE cPow(int num) {
    printf("我的C函数已被调用\n");
    printf("参数 num = %d\n", num);
    return num * num;
}

#ifdef __cplusplus
}
#endif

编译到 js

emcc callc.c -s WASM=1 -s "EXTRA_EXPORTED_RUNTIME_METHODS=['ccall']" -o callc.js

创建 callc.html 文件, 加载 js 文件并调用方法

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Web Assembly</title>
</head>
<body>
    <script src="./callc.js"></script>
    <script>
        window.onload = () => {
            var result = Module.ccall(
                'cPow', // C函数名
                'number', // 返回值类型
                ['number'], // 各参数类型
                [10], // 各参数值
            );
            console.log(`返回值 result = ${result}`);
        }
    </script>
</body>
</html>

打开页面, 查看 console

减小生成的 js 和 wasm 文件体积

前面的第一个例子, 生成的文件体积如下图

image.png

添加优化参数并重新执行命令生成, emcc hello.c -s WASM=1 -O3 -o hello.js, 这次生成的文件体积如下图

image.png

另外生成的文件体积与 C 代码中依赖的标准库数量是成正比的, 用到 strlen, flockfile, funlockfile, memcpy, fwrite, fputs 等方法越多, 生成的提交越大

参考链接

developer.mozilla.org/zh-CN/docs/… kripken.github.io/emscripten-…