たまにはまとまってない情報を書いてもいいじゃないかということで、表題の通り暫定版です。
Nodejsのclusterモジュールのドキュメントを読んでいて、
「ほぉ、並列化って簡単にできるんだなぁ」と感じつつ、関連記事をいろいろ調べてみると、
的な記述が色々あり、違和感を感じました。
Goなどのマルチスレッドができる言語でやる”並列化”と私が調べているものは別物なのでは? と。
ということで、身近な頼れる方々へ聞いて調べて考えた結果の暫定的な理解を書き留めておきます。 理解に誤りがあったら指摘もらえると喜びます。
パフォーマンスチューニングやスレッドセーフであるための4条項とかそういう込み入った話ではなく、浅い話です。
たとえば、Goで並列処理をする場合、goroutineを使用すると思います。
jsで複数の非同期処理を同時に行おうとした場合、Promise.allを使用すると思います。
Herokuの記事によると、Nodeでワーカーの並列度を最適化するには、throngなどのクラスタリングマネージャを使用して、マルチプロセス化したら良いと思う的なことが書かれています。
一体なにがどれにあたるんだ、違いがわからん と思っていたのですが、
JavaScript(Nodejs)はマルチコアな筐体で動作させたとしても、あくまで シングルスレッド な言語であるという点から整理すると、
と理解しました。 多重化と並列化は置いといて、マルチプロセスについてより詳細にまとめていきます。
@L_e_k_o 何をやろうとしてるかわからんけど、node なら 1 core 1 proc で Load Average がコア数に近似する性能を目指せばいいと思う。使ったことないけど rxjs-cluster っての見つけた。https://t.co/QjSFZfg1K9
— けん⚡ (@ken_zookie) 2016年7月15日
@L_e_k_o top コマンドで見える LoadAverage の値がCPUの数と同じであればちょうどCPUの性能を使いきれている、という見方ができる。過負荷試験とかで、どのプロセス数のパターンが最も優れているかの指標になる。
— けん⚡ (@ken_zookie) 2016年7月15日
頼れるパイセンがアドバイスをくれた。
なんとなーくCPUのコア数と同じって理解だったのが、
LoadAverage
という値がCPUのコア数と同じになるように調整すれば良いと判明。
なのでプロセスの数自体はCPUのコア数と必ずしも一致しない模様。
load averageとは ロードアベレージはシステム全体の負荷状況を表す指標。
「1CPUにおける単位時間あたりの実行待ちとディスクI/O待ちのプロセスの数」で表される。
システムのスループットを上げたい場合はロードアベレージを下げることを目標にする。 load averageを見てシステムの負荷を確認する – Qiita
この引用部分だけでは 低ければ低いほど良い ように見えますが、
CPUコア数よりLoadAverageが高い場合は処理待ちが発生しており、逆に低すぎるとCPUパワーを余らせていることになる
なのでCPUのコア数より高い場合は下がるように改善を。
低すぎる場合は、余ってるマシンパワーを活かすようにプロセス増やしたりCPU負荷が高いけど高速な処理に変えたりと性能改善が可能
最後。Nodejsにはclusterやchild_processなどのモジュールが組込みモジュールとして提供されている。
これはもう「アプリケーションをマルチプロセス化して下さい」と言っているようなものなのではないか…?
Herokuもthrongという簡易クラスタリング用のライブラリを使った例を出していたりする。
phpにもpcntlというプロセス制御の拡張機能がある。 シングルスレッドの言語でもプロセスを並べれば、スレッドをロックするようなsleep関数等もプロセスマネージャが多重化してくれるので、他の処理を行える。 ただし、PHPにはPHP-FPMなどのプロセスマネージャもある。
実装は、たかだか15行程度のコードで可能。でも実装が必要で、メンテも必要。 これってプログラム言語のレイヤが担うべき責務なのか? ? 言語でやらずにプロセスマネージャを別途利用したほうが良いのか?? というのが最後の疑問。
ちなみにNodejsのサンプルコードはこちら:
const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;
if (cluster.isMaster) {
// Fork workers.
for (var i = 0; i < numCPUs; i++) {
cluster.fork();
}
cluster.on('exit', (worker, code, signal) => {
console.log(`worker ${worker.process.pid} died`);
});
} else {
// Workers can share any TCP connection
// In this case it is an HTTP server
http.createServer((req, res) => {
res.writeHead(200);
res.end('hello world\n');
}).listen(8000);
}
会社のパイセンに聞いてみたところ、
「並列処理とマルチプロセスは違う、マルチプロセスをやりたいなら言語レベルではなく、より上位でプロセスマネージャを利用したほうが良い。
なぜなら、本気でクラスタリングしたいなら、Nodeのサンプルコードのような簡素な実装ではままならず、とても複雑な考慮や制御が必要になるのでコストとリスクが高すぎるから」
と回答を得た。
「え、これ実装しなきゃ早くならないの? ? 実装汚れるなぁ…」と不安に思っていたので、独自実装はすべきでないという同じ方向性で良かった。
現時点で理解したことは以上です。