自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

壓榨計算機性能:瀏覽器和 Node.js 的異步計算

系統(tǒng) 瀏覽器
所有的軟件代碼最終都是通過 CPU 來跑的,能不能把 CPU 高效利用起來是區(qū)分性能高低的標志,也就是說不能讓它空轉(zhuǎn)。

[[421495]]

本文轉(zhuǎn)載自微信公眾號「神光的編程秘籍」,作者神說要有光。轉(zhuǎn)載本文請聯(lián)系神光的編程秘籍公眾號。

都說 Node.js 可以實現(xiàn)高性能的服務(wù)器,那什么是高性能呢?

所有的軟件代碼最終都是通過 CPU 來跑的,能不能把 CPU 高效利用起來是區(qū)分性能高低的標志,也就是說不能讓它空轉(zhuǎn)。

那什么時候會空轉(zhuǎn)呢?

  • 當程序在進行網(wǎng)絡(luò)和磁盤的 IO 的時候,這時候 CPU 是空閑的,也就是在空轉(zhuǎn)。
  • 多核 CPU 可以同時跑多個程序,如果只利用了其中一核,那么其他核也是在空轉(zhuǎn)。

所以,要想達到高性能,就要解決這兩個問題。

操作系統(tǒng)提供了線程的抽象,對應(yīng)代碼不同的執(zhí)行分支,都是可以同時上不同的 CPU 跑的,這是利用好多核 CPU 性能的方式。

而如果有的線程在進行 IO 了,也就是要阻塞的等待讀寫完成,這種是比較低效的方式,所以操作系統(tǒng)實現(xiàn)了 DMA 的機制,就是設(shè)備控制器,由硬件來負責(zé)從設(shè)備到內(nèi)存的搬運,在搬完了告訴 CPU 一聲。這樣當有的線程在 IO 的時候就可以把線程暫停掉,等收到 DMA 運輸數(shù)據(jù)完成的通知再繼續(xù)跑。

多線程、DMA,這是利用好多核 CPU 優(yōu)勢、解決 CPU 阻塞等 IO 的問題的操作系統(tǒng)提供的解決方案。

而各種編程語言對這種機制做了封裝,Node.js 也是,Node.js 之所以是高性能,就是因為異步 IO 的設(shè)計。

Node.js 的異步 IO 的實現(xiàn)在 libuv,基于操作系統(tǒng)提供的異步的系統(tǒng)調(diào)用,這種一般是硬件級別的異步,比如 DMA 搬運數(shù)據(jù)。但是其中有一些同步的系統(tǒng)調(diào)用,通過 libuv 封裝以后也會變成異步的,這是因為 libuv 內(nèi)有個線程池,來執(zhí)行這些任務(wù),把同步的 API 變成異步的。這個線程池的大小可以通過 UV_THREADPOOL_SIZE 的環(huán)境變量設(shè)置,默認是 4。

我們在代碼里調(diào)用的異步 API,很多都是通過線程來實現(xiàn)的。

比如:

  1. const fsPromises = require('fs').promises; 
  2.  
  3. const data = await fsPromises.readFile('./filename'); 

但是,這種異步 API 只解決了 IO 的問題,那如何利用多核 CPU 的優(yōu)勢來做計算呢?

Node.js 在 10.5 實驗性的引入(在 12 正式引入)了 worker_thread 模塊,可以創(chuàng)建線程,最終用多個 CPU 跑,這是利用多核 CPU 的做計算的方式。

異步 API 可以利用多線程做 IO,而 worker_thread 可以創(chuàng)建線程做計算,用于不同的目的。

要聊清楚 worker_thread,還得從瀏覽器的 web worker 聊起。

瀏覽器的 web worker

瀏覽器也同樣面臨不能利用多核 CPU 做計算的問題,所以 html5 引入了 web worker,可以通過另一個線程做計算。

我們創(chuàng)建一個 Worker 對象,指定跑在另一個線程的 js 代碼,然后通過 postMessage 傳遞消息給它,通過 onMessage 接收消息。這個過程也是異步的,我們進一步把它封裝成了 promise。

然后在 webWorker.js 里面接收數(shù)據(jù),做計算,之后通過 postMessage 傳回結(jié)果。

  1. <!DOCTYPE html> 
  2. <html lang="en"
  3. <head></head> 
  4. <body> 
  5.     <script> 
  6.         (async function () { 
  7.             const res = await runCalcWorker(2, 3, 3, 3); 
  8.             console.log(res); 
  9.         })(); 
  10.  
  11.         function runCalcWorker(...nums) { 
  12.             return new Promise((resolve, reject) => { 
  13.                 const calcWorker = new Worker('./webWorker.js'); 
  14.                 calcWorker.postMessage(nums) 
  15.                 calcWorker.onmessage = function (msg) { 
  16.                     resolve(msg.data); 
  17.                 }; 
  18.                 calcWorker.onerror = reject; 
  19.             }); 
  20.         } 
  21.     </script> 
  22.  
  23. </body> 
  24. </html> 

這樣,我們就利用了另一個 CPU 核來跑了這段計算,對寫代碼來說和普通的異步代碼沒啥區(qū)別。但這個異步實際上不是 IO 的異步,而是計算的異步。

Node.js 的 worker thread 和 web worker 類似,我甚至懷疑 worker thread 的名字就是受 web worker 影響的。

Node.js 的 worker thread

把上面那段異步計算的邏輯在 Node.js 里面實現(xiàn)話,是這樣的:

  1. const runCalcWorker = require('./runCalcWorker'); 
  2.  
  3. (async function () { 
  4.     const res = await runCalcWorker(2, 3, 3, 3); 
  5.     console.log(res); 
  6. })(); 

以異步的方式調(diào)用,因為異步計算和異步 IO 在使用方式上沒啥區(qū)別。

  1. // runCalcWorker.js 
  2. const  { Worker } = require('worker_threads'); 
  3.  
  4. module.exports = function(...nums) { 
  5.     return new Promise(function(resolve, reject) { 
  6.         const calcWorker = new Worker('./nodeWorker.js'); 
  7.         calcWorker.postMessage(nums); 
  8.  
  9.         calcWorker.on('message', resolve); 
  10.         calcWorker.on('error', reject); 
  11.     }); 

然后異步計算的實現(xiàn)是通過創(chuàng)建 Worker 對象,指定在另一個線程跑的 JS,然后通過 postMessage 傳遞消息,通過 message 接收消息。這個和 web worker 很類似。

  1. // nodeWorker.js 
  2. const { 
  3.     parentPort 
  4. } = require('worker_threads'); 
  5.  
  6. parentPort.on('message', (data) => { 
  7.     const res = data.reduce((total, cur) => { 
  8.         return total += cur; 
  9.     }, 0); 
  10.     parentPort.postMessage(res); 
  11. }); 

在具體執(zhí)行計算的 nodeWorker.js 里面,監(jiān)聽 message 消息,然后進行計算,通過 parentPost.postMessage 傳回數(shù)據(jù)。

對比下 web worker,你會發(fā)現(xiàn)特別的像。所以,我覺得 Node.js 的 worker thread 的 api 是參考 web worker 來設(shè)計的。

但是,其實 worker thread 也支持在創(chuàng)建的時候就通過 wokerData 傳遞數(shù)據(jù):

  1. const  { Worker } = require('worker_threads'); 
  2.  
  3. module.exports = function(...nums) { 
  4.     return new Promise(function(resolve, reject) { 
  5.         const calcWorker = new Worker('./nodeWorker.js', { 
  6.             workerData: nums 
  7.         }); 
  8.         calcWorker.on('message', resolve); 
  9.         calcWorker.on('error', reject); 
  10.     }); 

然后 worker 線程里通過 workerData 來?。?/p>

  1. const { 
  2.     parentPort, 
  3.     workerData 
  4. } = require('worker_threads'); 
  5.  
  6. const data = workerData; 
  7. const res = data.reduce((total, cur) => { 
  8.     return total += cur; 
  9. }, 0); 
  10. parentPort.postMessage(res); 

因為有個傳遞消息的機制,所以要做序列化和反序列化,像函數(shù)這種無法被序列化的數(shù)據(jù)就無法傳輸了。這也是 worker thread 的特點。

Node.js 的 worker thread 和 瀏覽器 web woker 的對比

從使用上來看,都可以封裝成普通的異步調(diào)用,和其他異步 API 用起來沒啥區(qū)別。

都要經(jīng)過數(shù)據(jù)的序列化反序列化,都支持 postMessage、onMessage 來收發(fā)消息。

除了 message,Node.js 的 worker thread 支持傳遞數(shù)據(jù)的方式更多,比如還有 workerData。

但從本質(zhì)上來看,兩者都是為了實現(xiàn)異步計算,充分利用多核 CPU 的性能,沒啥區(qū)別。

總結(jié)

高性能的程序也就是要充分利用 CPU 資源,不要讓它空轉(zhuǎn),也就是 IO 的時候不要讓 CPU 等,多核 CPU 也要能同時利用起來做計算。操作系統(tǒng)提供了線程、DMA的機制來解決這種問題。Node.js 也做了相應(yīng)的封裝,也就是 libuv 實現(xiàn)的異步 IO 的 API,但是計算的異步是 Node 12 才正式引入的,也就是 worker thread,API 設(shè)計參考了瀏覽器的 web worker,傳遞消息通過 postMessage、onMessage,需要做數(shù)據(jù)的序列化,所以函數(shù)是沒法傳遞的。 

從使用上來看異步計算、異步 IO 使用方式一樣,但是異步 IO 只是讓 cpu 不同阻塞的等待 IO 完成,異步計算是利用了多核 CPU 同時進行并行的計算。

 

責(zé)任編輯:武曉燕 來源: 神光的編程秘籍
相關(guān)推薦

2022-08-21 07:30:55

程序并發(fā)Golang編碼

2022-01-04 21:36:33

JS瀏覽器設(shè)計

2021-12-08 07:55:41

EventLoop瀏覽器事件

2012-03-09 09:11:29

Node.js

2021-03-04 23:12:57

Node.js異步迭代器開發(fā)

2020-09-15 08:26:25

瀏覽器緩存

2009-04-01 08:56:59

2021-04-06 10:15:29

Node.jsHooks前端

2021-03-16 16:16:41

GeneratorWebsockets前端

2011-12-23 13:58:57

node.js

2025-01-13 00:00:00

2019-07-09 14:50:15

Node.js前端工具

2020-12-08 06:28:47

Node.js異步迭代器

2021-09-07 07:53:43

工具

2021-08-12 15:00:01

Linux終端

2019-09-19 09:30:53

量子計算機芯片超算

2021-01-26 08:07:44

Node.js模塊 Async

2013-11-01 09:34:56

Node.js技術(shù)

2015-09-30 11:22:19

計算機大數(shù)據(jù)

2023-10-11 18:30:39

Web系統(tǒng)程序
點贊
收藏

51CTO技術(shù)棧公眾號