あいつの日誌β

働きながら旅しています。

process.nextTick() の使い所

2012/09/12 追記

WaFのresponse自体が重い処理だったりするので一回そこで次の人に順番回してあげてねっていう意味でした。

というわけで以下の記事は忘れてもらっていいすかねえ...

process.nextTick() の使い所

ある日上長から「重たい処理をreturn する直前にprocess.nextTick()を挟んでね」って言われた時の話です。

すぐに終わる仕事なので順番を譲って欲しい

process.nextTick()を使ったからといって処理が高速になるわけではありません。
イベントループを長く止める処理がある場合にprocess.nextTickを使うといいらしい。

たとえばあなたの会社にいつも100枚単位でコピーをとっている人が居たとします。
あなたはコピーを1枚とりたいだけなのですがおとなしく待つ事にしました。

しばらくすると100枚コピーが完了しました。あなたにコピーをとるチャンスがやってきた...
と思ったら続けてその人は次のコピーを始めました。

「課長とA先輩とB先輩とC先輩にコピーを頼まれているです。」

コピー機を占有しないルール

というわけでコピー機を使い続ける事を禁止する仕組みがあったらうれしいです。

そこで「process.nextTick()を使ってね」と言われた話です。

でどこにprocess.nextTick()を書けばいいのかと聞いたら

「コピーが終わった直後に課長やら先輩やらにコピーしたものを渡す直前」という返答でした。

俺には理解不能だったのでベンチで検証する事にした

ということでコードを書いてみることにした

var async = require('async');

function getCopied(count, callback) {

    for (var i = 0; i < count; i++) {
        Math.random();
    }

    callback(null);
}

function getCopiedWithNextTick(count, callback) {

    for (var i = 0; i < count; i++) {
        Math.random();
    }

    process.nextTick(function() {
        callback(null);
    });
}

// short cut
function a(count, callback) {
    getCopied(count, callback);
}

function b(count, callback) {
    getCopiedWithNextTick(count, callback);
}

// !node-bench %
// run many times so that we can abstract out the overhead of promise creation.

//exports.countPerLap = count;

exports.compare = {
    noNextTick: function(done) {

        a(1000000, function() {});
        a(1000000, function() {});
        a(1000000, function() {});
        a(1000000, function() {});
        a(1000000, function() {});

        var tasks = [
            function(next) { a(10,   function() {next(null);}); },
            function(next) { a(10,   function() {next(null);}); },
            function(next) { a(10,   function() {next(null);}); },
            function(next) { a(10,   function() {next(null);}); },
            function(next) { a(10,   function() {next(null);}); },
            function(next) { a(10,   function() {next(null);}); },
            function(next) { a(10,   function() {next(null);}); },
            function(next) { a(10,   function() {next(null);}); },
            function(next) { a(10,   function() {next(null);}); },
            function(next) { a(10,   function() {next(null);}); }
        ];
        
        async.parallel(tasks, function() {
            done();
        });
    },
    withNextTick: function(done) {

        b(1000000, function() {});
        b(1000000, function() {});
        b(1000000, function() {});
        b(1000000, function() {});
        b(1000000, function() {});
        
        var tasks = [
            function(next) { b(10,   function() {next(null);}); },
            function(next) { b(10,   function() {next(null);}); },
            function(next) { b(10,   function() {next(null);}); },
            function(next) { b(10,   function() {next(null);}); },
            function(next) { b(10,   function() {next(null);}); },
            function(next) { b(10,   function() {next(null);}); },
            function(next) { b(10,   function() {next(null);}); },
            function(next) { b(10,   function() {next(null);}); },
            function(next) { b(10,   function() {next(null);}); },
            function(next) { b(10,   function() {next(null);}); }
        ];
 
        async.parallel(tasks, function() {
            done();
        });
    }
};

実行してみる

:!node-bench node/nexttick.js
benchmarking /Users/okamura/project/Scribbled/node/nexttick
Please be patient.

Scores: (bigger is better)

withNextTick
Raw:
 > 0.015857284440039643
 > 0.015968063872255488
 > 0.015920398009950248
 > 0.015873015873015872
 > 0.01582591493570722
Average (mean) 0.01588893542619369

noNextTick
Raw:
 > 0.01568627450980392
 > 0.015748031496062992
 > 0.015717092337917484
 > 0.01579466929911155
 > 0.015717092337917484
Average (mean) 0.015732631996162687

Winner: withNextTick
Compared with next highest (noNextTick), it's:
0.98% faster
1.01 times as fast
0 order(s) of magnitude faster

うーん。たしかに早くなっているようだ。

まとめ

nextTick()を仕込んだら確かに早くなっていました。
なので今度からprocess.nextTickを使うようにします。