使用 Emscripten 將 C 和 C++ 編譯為 WebAssembly

網頁功能: WebAssembly

使用 Emscripten 將 C 和 C++ 編譯成高效能的 WebAssembly (Wasm) 模組,供瀏覽器使用。

1. 現代 Emscripten 編譯旗標

使用 emcc 或 em++ 進行編譯和連結時,請一律使用現代模組化旗標:

  • -sEXPORT_ES6:發出標準 ES6 模組語法 (import/export),而非舊版全域物件。這會自動設定 -sMODULARIZE。
  • -sENVIRONMENT=web:移除 Node.js/殼層執行階段程式碼,盡可能縮減套件大小。
  • -sSTRICT:強制執行現代 Emscripten 預設值,並在遇到已淘汰的旗標時失敗。
  • -sALLOW_MEMORY_GROWTH:允許 WebAssembly 線性記憶體堆積在分配記憶體時動態擴充。
  • -Oz (或 -O3) + -flto:在發布子版本中,盡可能縮減酬載大小。

2. 避免封鎖主執行緒

如果呼叫 WebAssembly 可能會耗費大量時間 (即超過幾毫秒),則不應在主要瀏覽器執行緒上呼叫。

如要避免在主要瀏覽器執行緒上執行這類程式碼,可以採取以下幾種做法:

  1. 在 Web Worker 中執行整個 WebAssembly 程式。這通常是最簡單的解決方案,因為不需要設定 SharedArrayBuffer 存取權,也不需要變更程式來源。

  2. 使用 pthread 或 Wasm Worker。這樣一來,工作就能分配給多個 worker,但需要 SharedArrayBuffer 存取權,這可能需要新增 COOP/COEP HTTP 標頭。

  3. 重構 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