hakobera's blog

技術メモ。たまに雑談

Node.js の Cluster のベンチマークをとってみた

Node.js v0.6 から新規標準モジュールとして導入された Cluster のベンチマークを取ってみました。

測定環境

Server
CPU: AMD PhenomII X6 1090T (6コア)
MEM: DDR3 16GB (4GB*4)
Client
MacBook Pro 15 (Early 2011)
CPU: Intel Core i7 2.0GHz (4コア)
MEM: DDR3 8GB (4GB*2)
Network

1GigabitEther (同一セグメント)

テスト方法

テストスクリプト

cluster-bench.js

var cluster = require('cluster');
var http = require('http');
var numCPUs = parseInt(process.argv[2], 10);

if (cluster.isMaster) {
  // Fork workers.
  for (var i = 0; i < numCPUs; i++) {
    cluster.fork();
  }

  cluster.on('death', function(worker) {
    console.log('worker ' + worker.pid + ' died');
  });
} else {
  // Worker processes have a http server.
  http.createServer(function(req, res) {
    res.writeHead(200);
    res.end("hello world\n");
  }).listen(8000);
}
測定方法

ワーカを必要な数だけ起動

node cluster-bench.js n # nはワーカ数

その後、ApacheBench を5回実行し、その平均値を取る。

ab -n 10000 -c 200 http://servername:8000/

結果

コア数以上のワーカを起動した場合(n=7,8)も測ってみた。

(単位: requests/sec)

n 1 2 3 4 5 6 7 8
1回目 7380.28 14737.7 17161.38 17480.74 17621.82 17900.07 17543.07 17806.95
2回目 7039.88 14704.28 17100.9 17549.85 17930.77 17709.73 17491.9 17733
3回目 7479.57 14555.54 17205.59 17420.46 17605.32 17598.3 17327.33 17849.72
4回目 7553.31 14759.31 17248.57 17451.11 17667.13 18138.7 17820.31 17710.82
5回目 7360.04 14670.48 17269.42 17378.11 17060.33 17818.24 17699.15 17767.5
平均 7362.616 14685.462 17197.172 17456.054 17577.074 17833.008 17576.352 17773.598
平均値のグラフ

f:id:scalar:20111111152216p:image

考察

ここまででわかること。

・ワーカ数に応じたスループットの向上は実現できている
・ワーカ数が3以上はほぼ頭打ち(緩やかには伸びているので、クラスタの効果は出ているはず)
・17,000 req/sec あたりで頭打ちになっているのはベンチマーク環境の限界か?

どうやら処理が軽すぎると、ネットワークなど他の部分がネックになって、頭打ちになるようなので、意図的にCPUピーキーな処理をやってみた。
なにかと話題のフィボナッチ数列の計算で、CPUが90%付近に張り付くように再帰の回数を設定。

テストスクリプト(CPUピーキー版)
ar cluster = require('cluster');
var http = require('http');
var numCPUs = parseInt(process.argv[2], 10);

function fib(i) {
  if(i == 0 || i == 1) return i;
  return fib(i-1) + fib(i-2);
}

if (cluster.isMaster) {
  // Fork workers.
  for (var i = 0; i < numCPUs; i++) {
    cluster.fork();
  }

  cluster.on('death', function(worker) {
    console.log('worker ' + worker.pid + ' died');
  });
} else {
  // Worker processes have a http server.
  http.Server(function(req, res) {
    res.writeHead(200);
    fib(20);
    res.end("hello world\n");
  }).listen(8000);
}
結果
n 1 2 3 4 5 6 7 8
平均 3043.38 5919.17 8381.77 10963.63 13336.51 15659.73 15535.40 15346.00

f:id:scalar:20111111154805p:image

というわけで、きっちりとワーカ数に応じてスケールしていますね。
コア数以上のワーカを指定しても意味がないのもはっきりわかりました。

なんとなく思ったこと

・軽い処理の場合は、Nodeが速すぎて他の部分がネックになるので、ワーカ数はその辺りで調整すればよい
・重い処理がある場合、コアを目一杯使うと綺麗にスケールする
・Node.js の Cluster は細かい制御は難しい(カーネルによるロードバランシング)ですが、簡単にスケールできて便利