平行光線追蹤
這是一個使用 WebAssembly、Rust 和 wasm-bindgen
的執行緒範例,最終產生一個平行光線追蹤器示範。這個示範有許多運作的部分,而且不幸的是,它並不是最容易掌握的,但希望這能讓您稍稍體驗一下在網路上使用 Rust 執行緒和 Wasm 的感覺。
建置示範
執行緒 WebAssembly 的主要問題之一是 Rust 沒有發布啟用執行緒支援的預先編譯目標(例如,標準程式庫)。這表示您需要使用適當的 rustc 標誌重新編譯標準程式庫,即 -C target-feature=+atomics,+bulk-memory,+mutable-globals
。請注意,這需要 nightly Rust 工具鏈。
若要執行此操作,您可以使用 Cargo 讀取的 RUSTFLAGS
環境變數
export RUSTFLAGS='-C target-feature=+atomics,+bulk-memory,+mutable-globals'
若要重新編譯標準程式庫,建議使用 Cargo 的 -Zbuild-std
功能
cargo build --target wasm32-unknown-unknown -Z build-std=panic_abort,std
請注意,您也可以透過 .cargo/config.toml
設定此選項
[unstable]
build-std = ['std', 'panic_abort']
[build]
target = "wasm32-unknown-unknown"
rustflags = '-Ctarget-feature=+atomics,+bulk-memory,+mutable-globals'
在此之後,cargo build
應會產生啟用執行緒的 WebAssembly 檔案,並且標準程式庫也會被適當編譯。
最後一步是像平常一樣執行 wasm-bindgen
,而 wasm-bindgen
不需要額外的設定即可使用執行緒。您可以繼續透過 wasm-pack
執行它,例如。
執行示範
目前,需要使用 --target no-modules
或 --target web
標誌與 wasm-bindgen
一起執行執行緒程式碼。這是因為 WebAssembly 檔案會導入記憶體而不是匯出它,因此我們需要在這個時候掛鉤 wasm 模組的初始化,以提供適當的記憶體物件。此示範使用 --target no-modules
,因為 Firefox 不支援 worker 中的模組。
使用 --target no-modules
,您將能夠在每個 web worker 內部使用 importScripts
導入 wasm-bindgen
產生的 shim JS,以及使用主執行緒的共享記憶體實例呼叫 wasm_bindgen
初始化函式。預期的用法是主執行緒上的 WebAssembly 會將其記憶體物件張貼到所有其他執行緒以使用其實例化。
注意事項
不幸的是,目前在網路上使用執行緒執行 Wasm 有許多注意事項,雖然有些是 wasm-bindgen
特有的。這些是需要考慮和注意的一些部分,雖然我們一直在尋找可以改進的地方,因此如果您有任何想法,請提出問題!
-
瀏覽器中的主執行緒無法封鎖。這表示如果您在主執行緒上執行 WebAssembly 程式碼,您永遠無法封鎖,也就是說您無法取得互斥鎖。這是在網路上難以處理的極端限制,不過一個解決方案是在 web worker 中專門執行 Wasm,並在主執行緒上執行 JS。可以在所有執行緒上執行相同的 wasm,但您需要非常警惕與主執行緒的同步。
-
目前設定執行緒環境有點不順暢,感覺不是那麼流暢。例如,
--target bundler
不被支援,而且在主執行緒和 Worker 執行緒上都需要非常特定的墊片。這些是可以使用的,但有點脆弱,因為沒有標準的方法將 Web Worker 作為 Wasm 執行緒啟動。 -
沒有「執行緒」的標準概念。例如,標準函式庫沒有可行的途徑來實作
std::thread
模組。因此,沒有執行緒結束的概念,TLS 解構函數也永遠不會執行。我們公開了一個輔助函數__wbindgen_thread_destroy
,它可以釋放執行緒堆疊和 TLS。如果調用它,它必須是給定執行緒從 Wasm 模組中調用的最後一個函數。 -
第一個執行緒之後啟動的任何執行緒可能會在其初始化例程中隱式阻塞。這是我們設定執行緒堆疊和 TLS 空間的方式所引入的限制。這意味著,如果您嘗試在主執行緒中執行 Wasm 模組,在您已經在 Worker 中執行它之後,可能會失敗。
-
執行 WebAssembly 程式碼的 Web Worker 無法接收來自 JS 的事件。Web Worker 必須完全返回到瀏覽器(理想情況下應該偶爾這樣做)才能接收 JS 訊息等。這意味著像 rayon 執行緒池這樣的常見範例不能直接應用於 Web。Web 的意圖是所有長期阻塞都發生在瀏覽器本身,而不是在每個執行緒中,但是生態系統中許多利用執行緒的 crate 並不一定這樣設計。
這些注意事項主要都是從 Web 平台本身繼承而來的,在為執行緒設計應用程式時,它們非常重要。由於這些限制,您不太可能從現成的 crate 中拉取出來並「直接使用」。您需要確定事先仔細計畫,並確保諸如此類的陷阱不會在未來導致問題。不過,如前所述,我們一直在積極開發此支援,因此如果有人有關於如何改進的想法,或者如果 Web 標準發生變化,我們將盡力更新此文件!
瀏覽器要求
此示範目前應在最新的 Firefox 和 Chrome 版本中運作,其他瀏覽器也可能會跟進。請注意,執行緒和 SharedArrayBuffer
需要設定 HTTP 標頭才能正確運作。有關更多資訊,請參閱 MDN 上 文件的「安全性要求」以及 Firefox 的推出部落格文章。這意味著在本地開發期間,您需要適當地配置您的 Web 伺服器或在您的瀏覽器中啟用變通辦法。