使用 Emscripten 将 C 和 C++ 编译为高性能 WebAssembly (Wasm) 模块,以便在浏览器中运行。
1. 现代 Emscripten 编译标志
使用 emcc 或 em++ 进行编译和链接时,请始终使用现代模块化标志:
-sEXPORT_ES6:发出标准 ES6 模块语法(import/export),而不是旧版全局对象。这会自动设置-sMODULARIZE。-sENVIRONMENT=web:剥离 Node.js/shell 运行时代码,以最大限度地减小软件包大小。-sSTRICT:强制执行现代 Emscripten 默认设置,并在遇到已弃用的标志时失败。-sALLOW_MEMORY_GROWTH:允许 WebAssembly 线性内存堆在分配内存时动态扩展。-Oz(或-O3)+-flto:在发布 build 中针对最小载荷大小进行优化。
2. 避免阻塞主线程
如果对 WebAssembly 的调用可能需要花费大量时间(即超过几毫秒),则不应在主浏览器线程上调用。
为避免在主浏览器线程上运行此类代码,可以采用以下几种方法:
在 Web Worker 中运行整个 WebAssembly 程序。这通常是最简单的解决方案,因为它不涉及设置 SharedArrayBuffer 访问权限或更改程序源代码。
使用 pthread 或 Wasm Worker。这样,工作就可以在多个工作器之间共享,但需要 SharedArrayBuffer 访问权限,这可能涉及添加 COOP/COEP HTTP 标头。
重构 C/C++ 代码,避免长时间运行的调用。例如,将工作切分成可以从
requestIdleCallback调用的较小部分。
3. 注意运行时警告和错误
请务必实际测试生成的 WebAssembly 程序,并确保控制台上不会生成任何警告或错误。
在启用 -sASSERTIONS 的情况下构建和运行程序,并修复产生的任何运行时故障。
4. 尽可能避免动态链接
Emscripten 最适合与静态库 (.a) 搭配使用。共享库(.so、.dylib、.dll)在浏览器部署中不太有用,并且可能会使事情变得不必要地复杂。
5. 移植现有 C/C++ 库
将现有 C/C++ 库与标准构建系统集成时:
CMake 项目
使用 emcmake 和 emmake 封装 CMake 命令:
mkdir build && cd build
emcmake cmake .. -DCMAKE_BUILD_TYPE=Release
emmake make
Autoconf/Make 项目
使用 emconfigure 设置交叉编译工具链目标:
emconfigure ./configure --host=wasm32-unknown-emscripten --disable-shared
emmake make