JS 形式の設定ファイルを読み込むときは eval の代わりに new Function を使う
なぜか?
それはスコープチェーンが継承されるのを防ぐため。
具体的な例を見てみましょう。
以下のような、設定ファイルがあったとします。
[ conf1.js ]
{ val1: 'abc', val2: 2 }
これを読み込みます。
eval 版
var work = 1; var data = fs.readFileSync('/path/to/conf'); var conf = eval('(' + data + ')');
new Function 版
var work = 1; var data = fs.readFileSync('/path/to/conf'); var conf = new Function('return ' + data)();
上の場合、eval 版、new Function 版ともに同じ結果なので問題ありません。
しかし、次のような設定ファイルの場合
{ val1: 'abc', val2: work=2 }
eval 版だと、読み出し後に変数 work が破壊されます。
一方、new Function 版はスコープチェーンが切れているので work は 1 のまま。問題ありません。
nvm の新機能
今年も Node.js のユーザーカンファレンス「東京Node学園祭」が11月18日 (あと16日後)に開催されます。楽しみですねー。
以下、アドベントカレンダーの参加記事(19日目)になります。
nvm の新機能
nvm は Node.js をバージョン毎にインストールできる便利なツールです。先月、いくつか機能が追加されたので、そちらを紹介したいと思います。
インストール用ファイル
以前は nvm をインストールする時、github から git clone で取ってきて、手動で nvm.sh を実行したり .bash_profile を編集したりと面倒だったのですが、今はコンソールからコマンド一発でインストールできます。
curl https://raw.github.com/creationix/nvm/master/install.sh | sh
上のコマンドでインストールから .bash_profile の設定まで自動でやってくれます。
ls-remote
公開されている Node.js のバージョンを確認できます。
$ nvm ls-remote v0.1.14 v0.1.96 v0.4.1 v0.6.3 v0.7.7 v0.1.15 v0.1.97 v0.4.2 v0.6.4 v0.7.8 v0.1.16 v0.1.98 v0.4.3 v0.6.5 v0.7.9 v0.1.17 v0.1.99 v0.4.4 v0.6.6 v0.7.10 v0.1.18 v0.1.100 v0.4.5 v0.6.7 v0.7.11 v0.1.19 v0.1.101 v0.4.6 v0.6.8 v0.7.12 v0.1.20 v0.1.102 v0.4.7 v0.6.9 v0.8.0 v0.1.21 v0.1.103 v0.4.8 v0.6.10 v0.8.1 v0.1.22 v0.1.104 v0.4.9 v0.6.11 v0.8.2 v0.1.23 v0.2.0 v0.4.10 v0.6.12 v0.8.3 v0.1.24 v0.2.1 v0.4.11 v0.6.13 v0.8.4 v0.1.25 v0.2.2 v0.4.12 v0.6.14 v0.8.5 v0.1.26 v0.2.3 v0.5.0 v0.6.15 v0.8.6 v0.1.27 v0.2.4 v0.5.1 v0.6.16 v0.8.7 v0.1.28 v0.2.5 v0.5.2 v0.6.17 v0.8.8 v0.1.29 v0.2.6 v0.5.3 v0.6.18 v0.8.9 v0.1.30 v0.3.0 v0.5.4 v0.6.19 v0.8.10 v0.1.31 v0.3.1 v0.5.5 v0.6.20 v0.8.11 v0.1.32 v0.3.2 v0.5.6 v0.6.21 v0.8.12 v0.1.33 v0.3.3 v0.5.7 v0.7.0 v0.8.13 v0.1.90 v0.3.4 v0.5.8 v0.7.1 v0.8.14 v0.1.91 v0.3.5 v0.5.9 v0.7.2 v0.9.0 v0.1.92 v0.3.6 v0.5.10 v0.7.3 v0.9.1 v0.1.93 v0.3.7 v0.6.0 v0.7.4 v0.9.2 v0.1.94 v0.3.8 v0.6.1 v0.7.5 v0.9.3 v0.1.95 v0.4.0 v0.6.2 v0.7.6
バイナリインストール
nvm install v0.9.3 ←バージョンは任意
以前は Node.js をインストールする時、ダウンロードしたソースコードをローカルでコンパイルしていましたが、現在はビルド済みのバイナリが用意されているので、それを落とすだけでインストール完了します。うちの環境だと昔は5分ぐらいかかっていたのが、今は 15 秒で終わります。感動的!
(なお、環境にマッチしたバイナリファイルがない場合は、これまで通りソースコードからコンパイル/ビルドされます。)
copy-packages
こちらは結構前(去年の 7 月)に追加された機能ですが、指定したローカル Node バージョンのグローバルモジュールを現在のバージョンのグローバルモジュールとして再インストールします。
たとえば、v0.9.3 をインストール後、v0.9.2 に入れていたグローバルモジュールを v0.9.3 でも使えるようにするときは以下のように打ちます。
$ nvm copy-packages v0.9.2
ディレクトリのコピーではなく、ネットからの再インストールなので、設定ファイルなどを変更していた場合は再度書き直す必要があります。
Node.js を Ninja でビルド
こちらの記事に Node.js を Ninja でビルドする方法がわかりやすく書いてあったので試してみました。
http://d.hatena.ne.jp/jovi0608/20120905/1346831489
実行環境 (Mac OS X 10.7.4 Node.js v0.9.2-pre)
結果
$ ./configure --ninja $ time make real 3m14.051s user 9m43.755s sys 0m52.738s
$ ./configure $ time make -j 2 real 4m26.464s user 7m45.149s sys 0m55.281s
$ ./configure $ time make real 6m44.763s user 5m56.184s sys 0m45.123s
ninja さん、すごく速いです。すばらしい。
(9/9 追記)
実行マシンの CPU が 1.7 GHz Intel Core i5( コア数 : 2 スレッド数 : 4 ) なので make の並列度を 3 と 4 にあげて追加測定しました。
結果
$ ./configure $ time make -j 3 real 3m34.384s user 8m53.363s sys 1m2.937s
$ ./configure $ time make -j 4 real 3m29.327s user 10m1.844s sys 1m10.998s
おおう、並列度 3, 4 にするとかなり速くなりましたね。
Ninja さんは中で賢く CPU を調べて並列化してるのかな。
Node.js フロー制御モジュール速度比較(その2)
Node.js のバージョンを 0.9.1 、node-block のバージョンを 0.1.5 に上げてベンチマークを取り直しました。
(ベンチマーク用のコードは前回と同じです。)
実行環境 (Mac OS X 10.7.4 Node.js v0.9.1)
・async (v0.1.22)
・step (v0.0.5)
・node-block (v0.1.5)
$ time node bench-async.js real 0m0.849s user 0m0.824s sys 0m0.028s
$ time node bench-step.js real 0m0.266s user 0m0.245s sys 0m0.021s
$ time node bench-node-block.js real 0m0.273s user 0m0.259s sys 0m0.013s
今回、node-block と Step がほぼ並んでいます。
node-block は、V8 組み込みの bind 関数を取っ払ったら、かなり速くなりました。
Node.js フロー制御モジュール速度比較
Node.js のフロー制御モジュールといえば async と step が有名ですね。
私は node-block という自作モジュールを使っているのですが、速度的なパフォーマンスが気になったのでベンチマークをとってみました。
実行環境 (Mac OS X 10.7.4 Node.js v0.9.0)
・async (v0.1.22)
・step (v0.0.5)
・node-block (v0.1.4)
$ time node bench-async.js real 0m0.848s user 0m0.826s sys 0m0.027s
$ time node bench-step.js real 0m0.263s user 0m0.241s sys 0m0.022s
$ time node bench-node-block.js real 0m0.370s user 0m0.353s sys 0m0.018s
グラフは右に長いほど遅くなります。
step がとても速いです。
一応 async と node-block のフォローをしとくと、async は多機能で、 node-block は step より書きやすいと思います。
[ bench-async.js ]
'use strict'; var async = require('async'); var loopCount = 20000; (function serialLoop() { if (loopCount-- <= 0) return; async.series([ function (cb) { process.nextTick(cb); }, function(cb) { async.parallel([ function(cb){process.nextTick(cb)}, function(cb){process.nextTick(cb)} ], cb); }, function(cb) { async.parallel([ function(cb){process.nextTick(cb)}, function(cb){process.nextTick(cb)}, function(cb){process.nextTick(cb)} ], cb); }, function(cb) { async.parallel([ function(cb){process.nextTick(cb)}, function(cb){process.nextTick(cb)}, function(cb){process.nextTick(cb)}, function(cb){process.nextTick(cb)} ], cb); } ], serialLoop); })();
[ bench-step.js ]
'use strict'; var Step = require('step'); var loopCount = 20000; (function serialLoop() { if (loopCount-- <= 0) return; Step( function () { process.nextTick(this); }, function() { process.nextTick(this.parallel()); process.nextTick(this.parallel()); }, function() { process.nextTick(this.parallel()); process.nextTick(this.parallel()); process.nextTick(this.parallel()); }, function() { process.nextTick(this.parallel()); process.nextTick(this.parallel()); process.nextTick(this.parallel()); process.nextTick(this.parallel()); }, function() { serialLoop(); } ); })();
[ bench-node-block.js ]
'use strict'; var block = require('node-block').block; var loopCount = 20000; (function serialLoop() { if (loopCount-- <= 0) return; block( function() { process.nextTick(this.async()); }, function() { process.nextTick(this.async()); process.nextTick(this.async()); }, function() { process.nextTick(this.async()); process.nextTick(this.async()); process.nextTick(this.async()); }, function() { process.nextTick(this.async()); process.nextTick(this.async()); process.nextTick(this.async()); process.nextTick(this.async()); } )(serialLoop); })();
Eclipse (Juno) で javascript を書けるようにする
先月リリースされた最新版 Eclipse 4.2 (Juno) を試そうとしたのですが、今回から javascript 用パッケージが廃止されたらしく、Node.js 向けの環境を作るのに一手間かかりました。
以下、環境構築メモ。
Classic 版をダウンロードする
http://www.eclipse.org/downloads/
Eclipse を起動
以下の手順で Web Developer Tools プラグインをインストール
メニュー > Help > Install New Software
work with: Juno - http://download.eclipse.org/releases/juno を選択
プラグイン一覧から
Web, Xml, JavaEE and OSGi Enterprise Development
Eclipse Web Developer Tools ←をチェック
nextボタンでインストール
Eclipse 再起動
Node.js uncaughtException から Domain へ移行
今後 uncaughtException での実装は非奨励になり domain に置き換わるらしいです。
https://github.com/joyent/node/commit/e8af3405574dfee2cb8c11bf27195b774332db96
というわけで、移行のメモ。
process.on('uncaughtException', function(err) { console.log(err.message); }); // 何らかの処理
↓
var domain = require('domain'); var d = domain.create(); d.on('error', function(err) { console.log(err.message); }); d.run(function() { // 何らかの処理 });
ついでに 移行前と移行後のベンチマークをとってみました。
$ time node bench-uncaughtException.js real 0m9.801s user 0m8.197s sys 0m3.544s
$ time node bench-domain.js real 0m11.794s user 0m10.240s sys 0m3.445s
実行環境はMBA。Node のバージョンは v0.8.2 です。
(7/20追記: Node v0.8.3 もほぼ同じ結果でした)
グラフは右に長いほど遅くなります。
ちょっと無視できない差が出ていますが、Node.js の開発も過渡期でしょうし今後に期待。
[ bench-uncaughtException.js ]
var fs = require('fs'); var cnt = 0; process.on('uncaughtException', function(err) { cnt++; }); for (var i=0; i<200000; i++) { fs.readFile('not_found', 'utf-8', function(err, data) { if (err) throw err; }); }
[ bench-domain.js ]
var fs = require('fs'); var domain = require('domain'); var d = domain.create(); var cnt = 0; d.on('error', function(err) { cnt++; }); d.run(function() { for (var i=0; i<200000; i++) { fs.readFile('not_found', 'utf-8', function(err, data) { if (err) throw err; }); } });