iT邦幫忙

2023 iThome 鐵人賽

DAY 28
0
Modern Web

網頁的另一個大腦:從基礎到進階掌握 Web Worker 技術系列 第 28

使用多個 Web worker 進行平行運算 - workerpool

  • 分享至 

  • xImage
  •  

workerpool 是一個可以同時用在 node.js瀏覽器(web worker) 的套件,實作了 Thread pool 的概念,讓程式可以做到高效的平行運算

什麼是執行緒池 (Thread pool)?

Thread pool 可以視為一連串的運算單元合併起來的架構,下圖綠色部分共有六個 thread 可以分別執行不同的運算,每一個 thread 在瀏覽器中對應到的就是一個 web worker 線程

在一開始會有許多待辦的 tasks 放入 task queue 中,等待 thread pool 中有其中一個 thread 完成任務後,就會從 task queue 中拿取新的 taskthread pool 中執行,而 thread pool 中執行完的任務就會放到 completed tasks

https://ithelp.ithome.com.tw/upload/images/20231011/201626878kTl5GePpQ.png
圖片來源:wiki 執行緒池

workerpool 套件基本用法

接著讓我們來看看 workerpool 的一些基本用法

創建 thread pool

我們可以在主線程中,引入 workerpoolcdn 連結,接著使用 workerpool.pool,創建出 thread pool,其中的 maxWorkers 代表這個 thread pool 最多可以開啟幾個線程,如果沒有傳 maxWorkers 的話預設的上限會是 CPU 的核心數 - 1worker 線程

import workerpool from 'https://cdn.jsdelivr.net/npm/workerpool@6.5.0/+esm';

// 創建最多 8 個 worker 線程的 thread pool
const pool = workerpool.pool({ maxWorkers: 8 });

在 thread pool 中運算

創建出 pool 之後可以使用 pool.exec() 呼叫要執行的函式,每次執行 pool.exec() 時,背後就會自動創建出一個 worker 線程來執行任務的運算,以下在 worker 線程中執行加法,算完後才把結果 7 丟回主線程

function add(a, b) {
  return a + b;
}

pool
  .exec(add, [3, 4])
  .then(function (result) {
    console.log('result', result); // outputs 7
  })

瞭解了 workerpool 的基礎用法後,一樣讓我們藉由範例來看如何使用 workerpool 做到高效的平行運算吧

範例

目的

使用多個 web worker 組成的 workerpool 進行平行運算,確認執行速度是否變快

說明

  1. 範例中我們打算利用 web worker 尋找質數,程式中會有兩個變數:
  • numberCount
    決定在多少數字以下尋找質數,例如:numberCount: 10,找到的質數應該是 [2, 3, 5, 7]

  • workerCount
    使用 worker 的數量,這代表在 workerpool 中最多會使用到幾個 worker 線程做運算

  1. 使用電腦 CPU 核心數 (hardwareConcurrecy)
    hardwareConcurrecy 可以取得電腦中邏輯運算單元的數量,這也代表著理論上最多可同時運行的 worker 數量,例如:從我的電腦中可以查到 CPU 核心的數量是 10,而瀏覽器實作 hardwareConcurrecy 時通常會回傳較低數量的邏輯運算單元,所以在我的 chrome 瀏覽器中回傳的 hardwareConcurrecy 等於 8
    當勾選 使用電腦 CPU 核心數 (hardwareConcurrecy) 後,使用 worker 的數量(workerCount) 就會被固定成 hardwareConcurrecy 回傳的值

  2. 按下 Run 按鈕後會根據 尋找質數的上限數字(numberCount)使用 worker 數(workerCount) 在 worker 線程中找出所有的質數

  3. 執行完後,運算的時間 會以表格方式顯示出來

  4. 根據不同的 使用 worker 數(workerCount),查看執行效率的差異

範例 Demo
https://ithelp.ithome.com.tw/upload/images/20231012/20162687Whd7DvqJHm.png

創建 workerpool

一開始會根據填入的 使用 worker 數(workerCount),決定 workerpool 中最多可運行的 worker 線程數量

const pool = workerpool.pool({ maxWorkers: workerCount });

分割所有要拿來找出質數的數字

這裡會將 尋找質數的上限數字(numberCount)使用 worker 數(workerCount) 丟入 partition 函數中,分割出要尋找質數的數字群們,例如:
numberCount: 100 萬
workerCount: 10
會把 100 萬個數字分割成 10 個群組,第一個群是 1~10 萬,接著 10~20萬,直到最後一群組是 90~100萬

const partitionArray = partition({ numberCount, workerCount });

在 workerpool 中同時運行多線程尋找質數

接著使用 pool.exec() 於各 worker 線程中執行 尋找質數(findPrimes) 函式

const promises = partitionArray.map((partitionList) => {
  // 將分割出來的數字丟入 findPrimes 中
  return pool.exec(findPrimes, [partitionList]);
});
const results = await Promise.all(promises);
// 所有找到的質數 (ex. [2, 3, 5, ...])
const primes = results.flat();

結果

https://ithelp.ithome.com.tw/upload/images/20231012/201626872ccUFrPhev.png

  • 可以發現當使用 1 個 worker 時執行的時間最長
  • worker 數量逐步增多,整體運算的時間也會變快,大約到 電腦 CPU 核心數 (hardwareConcurrecy) (我的是 8 個) 時,執行時間最短
  • 雖然 worker 數量可以無限制地增加,但同時能使用 CPU 核心運算的資源應該是有限的,過多的 worker 數量反而會拖慢整體運算時間

小結

雖然這個範例沒有做到嚴格的數據統計,而且不同資料量、不同的運算時間,適合創建的 worker 線程數量可能也都不一定,但基本上以 電腦 CPU 核心數 (hardwareConcurrecy) 創建出 thread pool 線程的數量上限,應該可以充分運用 CPU 資源,縮短整體運算的時間

Reference

workerpool github repo
wiki 執行緒池
Number of Web Workers Limit


上一篇
封裝 Web worker 的套件 - Comlink
下一篇
在 Web worker 中使用 opencv.js
系列文
網頁的另一個大腦:從基礎到進階掌握 Web Worker 技術30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言