亞馬遜Prime Video中使用WebAssembly提高了效率

banq發表於2022-01-28

亞馬遜基於通過Prime Video通過 8,000 多種裝置型別向數百萬客戶提供內容,如遊戲機、電視、機頂盒和 USB 供電的流媒體。當想要進行更新時,這些裝置中的每一個都需要單獨的本機版本,從而在可更新性和效能之間做出艱難的權衡。

在過去的一年裡,亞馬遜一直在使用 WebAssembly (Wasm),這是一個允許用高階語言編寫的程式碼在任何裝置上高效執行的框架,以幫助解決這種權衡。

亞馬遜加入了位元組碼聯盟,該聯盟致力於開發基於 Wasm 等標準構建的安全、高效、模組化和可移植的執行時環境。

通過對 Prime Video 應用程式的某些元素使用 Wasm 而不是 JavaScript,將中檔電視的平均幀時間從 28 毫秒減少到 18 毫秒。最壞情況下的幀時間也從 40 毫秒減少到 25 毫秒。正在進一步降低。

為了在各種裝置上實現高效更新,同時仍然保持效能,Prime Video 應用程式有兩個部分:

  • 一個用 C++ 編寫的高效能引擎,儲存在裝置上:薄的 C++ 層,其中包括一個 JavaScript 虛擬機器 (VM) 和執行 Prime Video 應用程式所需的元件,這些元件處理輸入、媒體管道以及諸如網路訪問之類的過程、影像解碼和視窗事件處理。
  • 一個易於更新的元件,每次都下載應用程式啟動:包括應用程式程式碼,以及處理場景管理、動畫系統、圖形渲染、佈局和資源管理等的低階元件。

但是,在編寫高效能程式碼 (C++) 和編寫效能較差但我們可以輕鬆更新的程式碼 (JavaScript) 之間一直存在矛盾。

 

WebAssembly

Wasm 為提供比 JavaScript 更具表現力的程式語言(例如 C 或 Rust)提供了編譯目標。與 JavaScript 程式碼一樣,編譯後的 Wasm 二進位制檔案在 VM 上執行,該 VM 提供程式碼和硬體之間的統一介面,而與裝置無關。

Wasm 最初是為 Web 瀏覽器設計的,但現在在瀏覽器之外有 Wasm VM 的獨立應用程式,例如執行物聯網軟體、遊戲模組和伺服器端工作負載。

然而,亞馬遜不能只用 Rust 重寫 Prime Video 應用程式並在 Wasm VM 上執行它,因為它仍然需要在不支援 Wasm 的舊裝置和瀏覽器上執行。我們也不想只為新架構建立新應用程式,因為我們重視跨環境部署相同的應用程式。

這就是我們的新架構的樣子:

亞馬遜Prime Video中使用WebAssembly提高了效率

帶有 WebAssembly 的新架構。

Wasm 二進位制檔案與 JavaScript 程式碼一起部署,通過相同的全自動管道,可以在幾個小時內將程式從程式碼提交到在客戶的裝置上執行。

開關

上圖顯示了新架構,其中一個 Wasm VM 和一個 JavaScript VM 在不同的執行緒中執行。但是我們如何在不重寫應用程式的情況下從第一個架構過渡到第二個架構?

第一步是更新裝置上的內容以包含 Wasm VM,因此它現在可以執行給定軟體元件的兩個版本(僅 JavaScript 或 JavaScript 和 Wasm)。這使我們能夠逐步將 Wasm 元件釋出給一部分客戶。

我們必須修改 Prime Video 應用程式與這些元件的通訊方式。在高層次上,應用程式通過建立場景來工作——視覺場景的表示——它由實現特定於裝置的節點組成。主機節點(例如,檢視、影像、文字)是一種資料結構,它具有更新和渲染視覺場景元件所需的所有資訊。

在啟動時,我們檢查我們是否在有 Wasm 可用的裝置上執行。如果是這樣,我們會在 JavaScript 中建立輕量級主機節點,除了向 Wasm 虛擬機器傳送命令之外什麼都不做。處理這些命令時,會在 Wasm 中建立“真正的”主機節點。

我們使用訊息在兩個 VM 之間進行通訊,因為我們不希望 JavaScript VM 的工作中斷 Wasm VM 的工作。Wasm 元件的工作是在不中斷的情況下儘快更新節點並將幀泵出到螢幕上。

困難的部分是以保留 JavaScript 系統行為的方式進行此切換。我們有時不得不在新的 Wasm 版本中複製 JavaScript 渲染器的“不正確”行為,因為該應用程式在某些邊緣情況下依賴它。確保 JavaScript VM 程式碼永遠不會在錯誤的執行緒上呼叫任何危險函式也增加了額外的困難。

 

結果 

正如我所提到的,切換到 Rust 和 Wasm 提高了應用程式的幀速率穩定性和速度。為了實現可靠的每秒 60 幀幀生成的目標並改善輸入延遲,我們將把更多系統遷移到 Wasm,例如焦點管理和佈局。

Wasm VM 的總記憶體消耗,包括模組例項、環境和模組本身,最多為 7.5 兆位元組。通過將這些系統遷移到 Wasm,我們總共節省了 30 兆位元組的 JavaScript 堆記憶體。在我們部署的大多數裝置上,記憶體是一種稀缺資源,因此這是一個受歡迎的減少。

我們的 Wasm 模組的二進位制大小在壓縮時為 150 KB(在符號剝離後為 750 KB 未壓縮)。模組體積小,加上 VM 啟動時間快,意味著 Wasm 的加入不會影響應用啟動時間。

使用 Rust 使所有經驗水平的程式設計師都可以貢獻程式碼,而無需審閱者仔細檢查每一行是否存在安全隱患。我們信任編譯器,我們可以將程式碼審查的重點放在功能上,而不是語言的極端情況。

總體而言,我們認為對 Rust 和 WebAssembly 的投資得到了回報:經過一年的 37,000 行 Rust 程式碼,我們顯著提高了效能、穩定性和 CPU 消耗並降低了記憶體利用率。

相關文章