摘要:背景前一陣子開發的項目導入由于自己的代碼問題引起了個性能問題一個的文件轉換成數據大概要耗時雖然后面發現是某個使用頻率非常高的函數內部用了構造函數造成的所以這里順便提醒一下如果你很在乎幾毫秒的差距的話建議謹慎使用哈但是在優化的過程中一度懷疑是
背景
前一陣子開發的項目 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人的前端團隊常年招兵買馬中,在廈門的和想來廈門的童鞋們,不要吝惜你的簡歷,使勁砸過來 郵箱:nuoya@gaoding.com, 期待你一起來稿事
原文地址 https://github.com/noahlam/ar...
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/101518.html
摘要:可以將耗時任務拆解出去,降低主線程的壓力,避免主線程無響應。主線程接收發消息每個實例通過接收消息,通過發送消息。收發主線程消息和主線程代碼類似,在代碼中,也是接收消息,這個消息來自主線程或者其它。 1 引言 本周精讀的文章是 speedy-introduction-to-web-workers,是一篇 Web Workers 快速入門的文章,借精讀這篇文章的機會,談談對 Web Wor...
摘要:組件之間的通訊分為三種父給子傳子給父傳兄弟組件之間的通訊父組件給子組件傳值子組件嵌套在父組件內部,父組件給子組件傳遞一個標識,在子組件內部用接收,子組件在模板里可以通過的形式進行使用。 組件之間的通訊分為三種 父給子傳 子給父傳 兄弟組件之間的通訊 1 父組件給子組件傳值 子組件嵌套在父組件內部,父組件給子組件傳遞一個標識,在子組件內部用props接收,子組件在模板里可以通過{{}}的...
摘要:組件之間的通訊分為三種父給子傳子給父傳兄弟組件之間的通訊父組件給子組件傳值子組件嵌套在父組件內部,父組件給子組件傳遞一個標識,在子組件內部用接收,子組件在模板里可以通過的形式進行使用。 組件之間的通訊分為三種 父給子傳 子給父傳 兄弟組件之間的通訊 1 父組件給子組件傳值 子組件嵌套在父組件內部,父組件給子組件傳遞一個標識,在子組件內部用props接收,子組件在模板里可以通過{{}}的...
摘要:組件之間的通訊分為三種父給子傳子給父傳兄弟組件之間的通訊父組件給子組件傳值子組件嵌套在父組件內部,父組件給子組件傳遞一個標識,在子組件內部用接收,子組件在模板里可以通過的形式進行使用。 組件之間的通訊分為三種 父給子傳 子給父傳 兄弟組件之間的通訊 1 父組件給子組件傳值 子組件嵌套在父組件內部,父組件給子組件傳遞一個標識,在子組件內部用props接收,子組件在模板里可以通過{{}}的...
閱讀 2662·2021-11-25 09:43
閱讀 2472·2021-09-22 15:29
閱讀 984·2021-09-22 15:17
閱讀 3628·2021-09-03 10:36
閱讀 2223·2019-08-30 13:54
閱讀 1740·2019-08-30 11:23
閱讀 1163·2019-08-29 16:58
閱讀 1290·2019-08-29 16:14