web worker 的傳值方式以及耗時對比
背景
前一陣子開發的項目 pptx 導入
, 由於自己的代碼問題,引起了個性能問題,一個 40p 的 pptx 文件,轉換成 json 數據,大概要耗時 60s+ ,雖然後面發現是某個使用頻率非常高的函數內部,用了 new Function 構造函數
造成的(所以這裏順便提醒一下,如果你很在乎幾毫秒的差距的話,建議謹慎使用哈),但是在優化的過程中,一度懷疑是性能達到了瓶頸,所以嘗試了使用 web worker 去優化,由於是文件,一般內容都比較大,發現 web worker 在傳值這塊占用了大部分的時間,所以想開這篇來詳細聊聊.
兩種傳值方式
關於 web worker 的基本用於以及傳值方式,網上以及有一大堆介紹了,這裏就不贅述了,這裏我們重點來看一下同一個文件用兩種方式來傳值,會有多大的差別,這邊隨意從電腦裏面找了一個 96MB 的 PSD 文件來測試.
主線程
fetch(‘./case.psd‘).then(file => { return file.blob(); }) .then(blob => { return new Promise(resolve => { let fileReader = new FileReader(); fileReader.onload = e => { resolve(e.target.result); } fileReader.readAsArrayBuffer(blob); }) }) .then(buf => { let worker = new Worker(‘1.js‘); console.time(‘計算時間‘); worker.postMessage(buf); worker.onmessage = e => { console.timeEnd(‘計算時間‘); } })
worker(子)線程, 這裏為了避免不必要的因素幹擾,worker 線程裏面什麽也不做,在收到消息後,直接 post 一個消息回去
self.onmessage = e => {
postMessage(0);
}
這邊我直接用 FileReader 的 readAsArrayBuffer,讀出來是一個長度為 96,138,230 的字符串,長度大概 0.96 億, 耗時大概 70ms 左右(同一個臺電腦取 10 次平均值,下同)
我們稍微改一下上面主線程的代碼,改用 轉移數據
的方式
- worker.postMessage(buf); + worker.postMessage(buf, [buf]);
同樣的數據, 耗時大概 17ms 左右,這 17ms 好像是個固定值,我嘗試換了個 800MB+ 的文件和一個裏面啥都沒有的空文本文件,大概都是這個時間.
不同的數據類型,用值傳遞的耗時也是不一樣的
fetch(‘./case.psd‘).then(file => {
return file.blob();
})
.then(blob => {
return new Promise(resolve => {
let fileReader = new FileReader();
fileReader.onload = e => {
resolve(e.target.result);
}
fileReader.readAsText(blob);
})
})
.then(str => {
console.log(str.length);
let worker = new Worker(‘1.js‘);
console.time(‘計算時間‘);
worker.postMessage(str);
worker.onmessage = e => {
console.timeEnd(‘計算時間‘);
}
})
這裏我們改用 FileReader 的 readAsText,讀出來是一個長度為 95,855,954 的字符串,長度大概 0.95 億, 耗時大概 118ms 左右,同樣我換了上面那個裏面啥都沒有的空文本文件,耗時也是 17ms 左右.
那我們試試用 readAsDataURL 看看讀出來的數據要多久
fetch(‘./case.psd‘).then(file => {
return file.blob();
})
.then(blob => {
return new Promise(resolve => {
let fileReader = new FileReader();
fileReader.onload = e => {
resolve(e.target.result);
}
fileReader.readAsDataURL(blob);
})
})
.then(str => {
console.log(str.length);
let worker = new Worker(‘1.js‘);
console.time(‘計算時間‘);
worker.postMessage(str);
worker.onmessage = e => {
console.timeEnd(‘計算時間‘);
}
})
讀出來是一個長度為 128,184,345 的字符串,長度大概 1,28 億, 耗時大概 85ms 左右(雖然字符串長度更長,但是耗時卻更短)
以上耗時,均為主線成向 worker 線程單向傳遞數據的耗時.
結論
- 轉移數據幾乎是零開銷(因為和傳遞空字符串的耗時是差不多的).
- 值傳遞的話,不同的數據類型,耗時也有差別,ArrayBuffer < base64 < 普通字符串.
- postMessage 傳遞消息,除了發送數據的耗時外,還有其他開銷(就是上面的 17ms). 當然每臺電腦性能不一樣,耗時也是不一樣的,不過按比例來看,這個占比還挺大的.
關於轉移的缺點, 網上也是有很多的, 這裏也就不啰嗦了, 總結一句就是數據無法同時在2個線程上使用.
另外個人覺得如果是普通的數據,為了轉移而去轉換成 Transferable objects
的話, 大部分情況下是劃不來的, 因為你需要在花在編碼解碼上的時間,會比直接傳遞花的時間多.
另外, 如果你是要用子線程處理圖片的話, ImageBitmap
格式 配合最近新鮮出爐的 OffscreenCanvas
也許是不錯的選擇.前提是你不需要考慮兼容性問題.
最後是廣告時間
我們40人的前端團隊常年招兵買馬中,在廈門的和想來廈門的童鞋們,不要吝惜你的簡歷,使勁砸過來 郵箱:[email protected]
, 期待你一起來稿事
原文地址 https://github.com/noahlam/ar...
web worker 的傳值方式以及耗時對比