Quantcast
Channel: Node.jsタグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 8882

Node.js: worker_threadsのスレッド間通信は、child_processのプロセス間通信の2〜11倍速い。

$
0
0

Node.jsでもマルチスレッドプログラミングができるworker_threadsというモジュールがあります。

語弊がありますが似たようなモジュールにchild_processというものもあります。本稿では、worker_threadsとchild_processをワーカー間通信の速さという観点で比較していきます。

本稿でわかること

  • child_processとworker_threadsどっちの通信速度のほうが速いか?

child_processは古参モジュールとして、マルチコアでの分散処理を支えてきた

worker_threadsは比較的新しいモジュールで、スレッドがNode.jsに導入される以前は、マルチコア環境のリソースを活かすには、Node.jsでは複数のプロセスを起動して負荷分散するというアプローチが取られてきました。Node.jsでマルチプロセス型の分散処理をするためによく使われるのが、child_processやclusterといったモジュールです。

worker_threadsもchild_processも似たようなワーカー間通信ができる

worker_threadsもchild_processも、処理をフォークして並列処理できるだけでなく、親ワーカーと子ワーカーの通信ができます。用語が多少異なりますが、worker_threadsの場合は、親スレッドと子スレッドとの間でデータを送受信できます。child_processの場合も、親プロセスと子プロセスの間でデータの送受信が可能です。

worker_threadsで、親スレッドから子スレッドにデータを送信する例:

const{Worker}=require('worker_threads')constworker=newWorker('./worker.js')worker.postMessage('Hello!')

child_processで、親プロセスから子プロセスにデータを送信する例:

const{fork}=require('child_process')constchildProcess=fork('./worker.js')childProcess.send('Hello!')

どちらも似たようなワーカー間通信ができるのがコードからも分かるかと思います。

ちなみに、worker_threadstとchild_processの2つは、そもそもアーキテクチャが異なるので、通信方法もデータのシリアライズ方法は異なります。そのへんの違いについては、簡単な比較表を載せておきます:

Pasted_Image_2020_03_12_12_36.png

どちらのワーカー間通信のほうが速い?

マルチコアを生かした分散処理をしようとすると、今や選択肢として歴史の深いchild_processと、新機能のworker_threadsの2つの選択肢があるわけですが、ワーカー間通信の効率という観点ではどちらが優れているのでしょうか? 気になって検証しました。

検証方法

検証方法としては下記のとおりです。

  • 親ワーカーと子ワーカー間で、N回メッセージの送受信を繰り返す。
  • そのN回の送受信にかかる時間を測定する。
  • 送受信するデータのパターンをいくつか用意し、データの内容によってどういう違いがでるかもついでに調べる。
  • 各データパターンごとに1回ずつ測定。

検証コード

検証するために書いたコードが下記です。

child-process.js
const{repeat,data}=require('./config.js')const{fork}=require('child_process')if(process.send===undefined){// parent processletcount=0fork(__filename).on('message',function(message){if(message==='end'){console.timeEnd('test')this.kill()return}elseif(message==='start'){console.time('test')}this.send(++count<=repeat)})}else{process.send('start')process.on('message',continues=>{process.send(continues?data:'end')})}
worker-threads.js
const{repeat,data}=require('./config.js')const{isMainThread,Worker,parentPort}=require('worker_threads')if(isMainThread){letcount=0newWorker(__filename).on('message',function(message){if(message==='end'){console.timeEnd('test')this.terminate()return}elseif(message==='start'){console.time('test')}this.postMessage(++count<=repeat)})}else{parentPort.postMessage('start')parentPort.on('message',continues=>{parentPort.postMessage(continues?data:'end')})}

パラメータは共通して設定できるように別ファイルにしました:

config.js
module.exports={repeat:1000,data:true,// data: 'a',// data: Array(10000).fill('x')// data: 'a'.repeat(100000),// data: Array(10000).fill({a: 1}),}

検証結果

測定結果としては、下記のグラフのようになりました。

Pasted_Image_2020_03_13_14_30.png

3つ目の1万要素ある配列を送受信するのを除くと、worker_threadsのほうがchild_processより2〜11倍速いということがわかりました。


Viewing all articles
Browse latest Browse all 8882

Trending Articles