使用Emscripten編譯Eigen演算法模組為WebAssembly
本文已參與「新人創作禮」活動,一起開啟掘金創作之路。
問題場景
在Web3D開發當中,我們面臨這樣一些問題:團隊中已經有成熟的演算法模組,通常使用C/C++編寫,我們需要實現同樣的功能,如何快速實現功能,並且保持功能的一致性和長期的可維護性。
技術選型
- 一方面我們可以使用前端Js的各類數學運算庫如:math.gl和gpu.js,對已有的演算法模組進行Js版本的重新實現
- 使用WebAssembly,通過輔助的編譯工具如emscripten將已有的演算法模組編譯為前端可直接呼叫的模組
考慮到我們已經有成熟的C++演算法模組,此時如果使用方案1,會帶來較大的人力成本,並且在開發過程中,並不能確保邏輯的一致性,因此使用選用WebAssembly
實現
根據emscripten的官方教程進行emscripten的安裝,再進行cmake的安裝
基於Emscripten,有兩種實現方案: 1. emcc直接命令列編譯,適用於依賴較少的情況,這次我們遇到的場景只需要用到Eigen,並且Eigen只需要以標頭檔案的形式引入 2. emcmake編譯 適用於依賴較多的情況,如使用Eigen,OpenCV做一些影象處理時,推薦編寫CMakeFile,維護起來更方便
下面對這兩種編譯方式進行介紹,讀者可按照上述兩種方式按需選擇
使用emcc
emcc -I ./eigen/ main.cpp -o main.js -s EXPORTED_FUNCTIONS="['_solve','_free']" -s WASM=1 -s EXPORTED_RUNTIME_METHODS="['setValue','getValue']"
上述是編譯Eigen演算法模組的emcc命令,解釋一下其中引數的含義,-I指gcc需要包含的標頭檔案路徑,main.cpp是主cpp檔案,-o指輸出,同時可以指定gcc優化級別,如果要使用wabt進行檢視,就不要使用O3了
-s指options:
EXPORTED_FUNCTIONS:指希望匯出的函式,需要加下劃線
EXPORTED_RUNTIME_METHODS :指需要匯出的Js runtime函式,根據使用的emscripten版本不同,並非所有的情況下,膠水JS程式碼都能完美的匯出所有C++模組需要用到的函式,這個時候就需要用到wabt工具進行除錯,沒有的函式需要補充上,github連結附在最後
使用cmake
``` cmake_minimum_required(VERSION 2.8) project(STEREO)
set(THIRDPARTY ${CMAKE_SOURCE_DIR}/../ThirdParty)
wasm編譯指令
set(EMSCRIPTENOPTIONS "SHELL:-s EXPORTED_FUNCTIONS=['_TriangulateDLTNView','_test'] -s EXPORTED_RUNTIME_METHODS=['ccall','cwrap'] --no-entry") set(CMAKE_BUILD_TYPE Release) set(CMAKE_CXX_FLAGS_RELEASE "-O1") set(CMAKE_CXX_STANDARD 17)
配置Eigen
include_directories(${THIRDPARTY}/Eigen) add_executable(STEREO main.cpp) target_link_options(STEREO PRIVATE ${EMSCRIPTENOPTIONS}) ``` 需要注意的是,最後的target_link_options,在這裡可以連結上所需的emscripten的編譯引數 編譯成功後我們會得到:
其中main.wasm編譯出的wasm檔案,main.js是膠水js程式碼,main.js中預設通過fetch的方式進行載入,我們可以直接在html中引入
完成編譯以後,進行Js與wasm模組的資料傳遞(函式傳參),官方推薦使用arraybuffer進行傳參,這裡再介紹一種方式,
JS引數,傳入到cpp(wasm)中
function getPoint(arr)
{
const BYTES=8;
const point = Module._malloc(arr.length * BYTES);
for(let i=0;i<arr.length;i++)
{
Module.setValue(point+i*BYTES, arr[i], 'double')
}
return point;
}
可以將js中的資料塊,以指標的形式傳入到cpp中
cpp計算結果,JS讀取:
我們定義的函式:
const char* solve(int rayLength, double* raw){}
在Js中定義函式Pointer_stringify ``` function Pointer_stringify(ptr, length) { if (length === 0 || !ptr) return ''; // TODO: use TextDecoder // Find the length, and check for UTF while doing so var hasUtf = 0; var t; var i = 0; while (1) { assert(ptr + i < TOTAL_MEMORY); t = HEAPU8[(((ptr)+(i))>>0)]; hasUtf |= t; if (t == 0 && !length) break; i++; if (length && i == length) break; } if (!length) length = i;
var ret = '';
if (hasUtf < 128) {
var MAX_CHUNK = 1024; // split up into chunks, because .apply on a huge string can overflow the stack
var curr;
while (length > 0) {
curr = String.fromCharCode.apply(String, HEAPU8.subarray(ptr, ptr + Math.min(length, MAX_CHUNK)));
ret = ret ? ret + curr : curr;
ptr += MAX_CHUNK;
length -= MAX_CHUNK;
}
return ret;
}
return Module['UTF8ToString'](ptr);
}
``` 注意,在17年後,emscirpten預設取消匯出了上述函式,因此上述函式不會預設存在於膠水JS檔案中,Module物件是Js膠水程式碼中定義在全域性上的,我們可以通過這個函式完成。
呼叫函式
最後我們呼叫:我們匯出的solve函式
const rawPoint = getPoint(data)
const targetPoint = _solve(2, rawPoint);
const result = Pointer_stringify(targetPoint);
第一行的rawPoint就是JS資料在js和wasm共享記憶體中的指標,傳到solve函式中,最後取指標按指標地址順序讀取資料得到最後結果
總結
在面臨此類問題的時候,團隊在做技術選型的時候,需要考慮一下幾個問題,再決定是否需要使用WebAssembly: 1. 開發成本:團隊中是否有人力進行WebAssembly的開發,開發工作由前端承擔還是由後端演算法自己維護,如果由前者承擔,在市面上相關技術人員並不充足的情況下,要保持好技術沉澱,梳理出一套較為通用的編譯流程,對於大多數團隊而言,如果由後端演算法承擔,需要讓開發人員理解前端模組化知識,通常直接編譯出的Js膠水檔案可能需要修改,在定義函式,指標傳參等知識點上,演算法同學也有一些理解成本,團隊應該權衡考慮。 2. 場景選擇,在做技術選型的時候,應該首先明確功能是否應該由前端承擔,對於一些高效能運算的C++演算法模組,如果使用者的場景是用完即走,則使用者通常對WebAPP的效能要求較高,此時不適合在前端做一些很重的計算,讓使用者等待太久,如在前端用PCL庫做耗時較高的重建並不是什麼明智的選擇,首先應當考慮的還是在服務端計算,網路傳輸結果;對於一些工具類的,如三維家這種一次載入,長時使用的,使用WebAssembly去移植已有的CAD系統,顯然是一種很不錯的選擇。
wasm除錯工具wabt :http://github.com/WebAssembly/wabt 可以通過wabt對編譯出的wasm檔案進行除錯,可以看到匯出了哪些函式
- 使用Emscripten編譯Eigen演算法模組為WebAssembly
- 俄羅斯一夜風雲!莫斯科交易所將暫停英鎊交易!俄股市也閃崩10%,發生了什麼?
- 俄羅斯 Yandex 被黑,造成莫斯科交通堵塞
- 俄羅斯打車 App 被黑,導致莫斯科交通一度陷入混亂僵局
- 俄羅斯作業系統生產商 Astra Linux 計劃在莫斯科上市
- Google被莫斯科法院處以1500萬盧布罰款
- 俄開發出全天候太陽能電池板
- 2022年莫斯科地鐵行業發展現狀分析 地鐵數量全俄第一
- 莫斯科不相信抄底
- 莫斯科交易所連續兩日閉市 亞太股指全線收紅
- 共享辦公平臺 WeWork 稱將繼續吸引付費使用者,不需要離開莫斯科
- 家只有9平米:莫斯科年輕人“住在盒子裡”
- 莫斯科地鐵究竟有多豪華?
- 國際空間站“星辰”號服務艙內冒煙觸發警報
- 城市收藏夾|魂斷莫斯科
- 莫斯科市法院:Google公司將被罰款600萬盧布
- 21%的莫斯科人認為加密貨幣和數字貨幣將在10年內取代法幣
- Yandex機器人開始在莫斯科中部送餐
- 黑客在莫斯科打開了 2732 個快遞櫃
- 阿莫斯CEO丁春妹:想做一點大事,不一定改變世界,但要利國利民