web worker和主執行緒的資料交換效率初探

fuzhenn發表於2017-12-25

概述

web worker是瀏覽器的多執行緒機制,多用於處理不涉及DOM操作的密集計算任務,例如演算法計算,資料請求處理等,我正在開發的開源地圖引擎 maptalks.js 中對地圖資料的處理就很適合放到web worker中(突兀的硬廣 ^_^)。

web worker最大的優點是形式簡單:

  • worker的執行上下文(context)與主執行緒獨立,無法互相呼叫,避免了多執行緒程式設計中的執行緒鎖等複雜機制
  • worker只能通過postMessage和onmessage與主執行緒交換資料

所以,worker程式的效能很大程度上取決於與主執行緒之間資料交換效率。

  • 預設情況下,postMessage中資料是採用結構化克隆(structrured clone)演算法來實現跨程式傳送的。
  • 引進Web Worker介面時,瀏覽器同時引進了Transferable和ArrayBuffer這一對好基友,當傳送的資料為Transferable時,瀏覽器能通過只傳送資料引用(指標),而不是結構化克隆(structrured clone)來極大的提高資料傳送效率,具體可以參考google的 Transferable Objects: Lightning Fast!(需翻牆)。

問題

當傳送資料結構不復雜時(型別化陣列,字串等),我們有兩種可選的資料傳送方式:

  • 直接用postMessage({ data })傳送,讓瀏覽器用structured clone演算法拷貝資料到主執行緒
  • 參考谷歌文章,將資料先轉化為ArrayBuffer(Transferable),然後直接傳引用給主執行緒,無需structured clone

如果資料本身已經是型別化陣列,毫無疑問方式二更好。

但如果有很多簡單資料(型別化陣列或字串),將這些資料轉化為ArrayBuffer後採用Transferable傳送,相比瀏覽器的structured clone,哪個效率更高?

測試

為此,我寫了一個jsperf測試程式

測試資料為100K位元組的整數陣列和200K位元組的字串陣列,建立以下四個測試用例:

  1. 封裝成物件,直接傳送資料 (structured clone傳送)
  2. 拷貝為新的陣列後,封裝成物件傳送 (structured clone傳送)
  3. 將陣列轉化為ArrayBuffer,轉化方式用傳統的遍歷迴圈資料 (Transferable傳送)
  4. 將陣列轉化為ArrayBuffer,轉化方式用TypedArray.prototype.set批量轉化 (Transferable傳送)

測試結果取決於瀏覽器:

  • 在chrome 64和firefox 58下, 都是方式4最快,但chrome上方式3最慢
  • firefox下方式1,2最慢,與3,4差距很大
  • 在ie 11下,1最快,3最慢,但4與1的差距並不大

結論

  • 綜合來看,資料結構簡單時,轉化成ArrayBuffer,用Transferable方式傳送效能更好
  • ArrayBuffer的轉化方式很重要,用Array.prototype.set,而不是迴圈遍歷
  • 除了100K + 200K,也測試了其他資料,結論仍然成立

最後附上我本機的測試結果:

Chrome 64:

web worker和主執行緒的資料交換效率初探

Firefox 58:

web worker和主執行緒的資料交換效率初探

IE11:

web worker和主執行緒的資料交換效率初探

相關文章