xxhash というハッシュアルゴリズムがあり、パフォーマンス的には最速とのことである。
js でも幾つかライブラリが用意されているため、容易に使うことができる。

ということで簡単なコード書いて測ってみた。winston には profile 機能もあるとのことなので
それもついでに試してみる。。

比較するライブラリは xxhash と、 xxhashjs 最後に、crypto で SHA1 を作成してみて
100,000回ハッシュしてみた時間を測定していみる。
xxhash は、 C で記述されているので、スピーディなはず。
xxhashjs については、pureJS なライブラリで、ブラウザ上でも使うことができる。

 

var xxhashjs = require('xxhashjs');
var XXHash = require('xxhash');
var crypto = require('crypto');
var n = 100000;
var winston = require('winston');

winston.profile('xxhash');
for (var i = 0; i < n; i++) {
    XXHash.hash(new Buffer('abcd'), 0x0108);
}
winston.profile('xxhash');

winston.profile('xxhashjs');
for (var i = 0; i < n; i++) {
    xxhashjs('abcd', 0x0108);
}
winston.profile('xxhashjs');

winston.profile('sha1');
for (var i = 0; i < n; i++) {
    crypto.createHash('sha1').update('abcd').digest('hex');
}
winston.profile('sha1');
info: xxhash duration=162ms
info: xxhashjs duration=144ms
info: sha1 duration=439ms

文字数が少ないと、xxhashjs のほうが有利か。

では、文字数が 1,000文字のものをハッシュするとなるとどうなるか。

var xxhashjs = require('xxhashjs');
var XXHash = require('xxhash');
var crypto = require('crypto');
var n = 100000;
var length = 1000;
var winston = require('winston');
var randomstring = require('randomstring');

winston.profile('xxhash');
for (var i = 0; i < n; i++) {
    XXHash.hash(new Buffer(randomstring.generate(length)), 0x0108);
}
winston.profile('xxhash');

winston.profile('xxhashjs');
for (var i = 0; i < n; i++) {
    xxhashjs(randomstring.generate(length), 0x0108);
}
winston.profile('xxhashjs');

winston.profile('sha1');
for (var i = 0; i < n; i++) {
    crypto.createHash('sha1').update(randomstring.generate(length)).digest('hex');
}
winston.profile('sha1');
info: xxhash duration=5107ms
info: xxhashjs duration=5324ms
info: sha1 duration=5216ms

若干、xxhashjs のほうが不利になるようだ。

node.js アプリケーション上で、メモリキャッシュを使いたい場合、いくつかのライブラリが存在する。

memory-cache

Github: https://github.com/ptarjan/node-cache

npm install memory-cache

すごくシンプルに実装されている。どこから require('memory-cache') をしても、その中の情報は共有される実装。

node-cache

Github: https://github.com/tcs-de/nodecache

npm install node-cache

memory-cache の Github レポジトリは node-cache で、こちらは nodecache という名前で管理されている。ややこしい。CoffeScript で書かれている。memory-cache に比べると高機能で、require('node-cache') では、データがオブジェクトに閉じ込められているため、複数のメモリ空間を別々に定義することができる。

get は、コールバックで取得する必要があったりする。

lru-cache

Github: https://github.com/isaacs/node-lru-cache

npm install lru-cache

LRU (Least Recently Used) 方式でキャッシュを管理できるライブラリ。つまり、予め利用件数を定めておいて、使われない情報を削除することができる。メモリ容量を管理したい場合は必要そうなライブラリ。

こちらも、node-cache と同様に、複数の空間を管理することができそうだ。

早さとかを比べてみる

1,000,000 回の get/set を行った結果

  • memory-cache: 1,071 ms
  • node-cache: 269,424 ms
  • lru-cache: 4,731ms

node-cache は高機能なだけあって、遅い気がしますねー。。。(計測の仕方がダメだった可能性もあるけど、1件の状態でも10ms 以上かかってしまっている。) //TODO: 個人的には、lru-cache の set() に、キャッシュごとに expire を定められたら使えるなーと思ったりしました。

ここのところ、仕事で node.js を使っているので、利用したライブラリなどを少しずつ紹介していきたいと思う。

winston は、ロギングライブラリで、非同期にログを書き出すことができる。

Sails.js は、このライブラリを利用してログをとっているし、このライブラリはnodejitsu社が、flatironというフレームワークのために開発したものということ。作りもしっかりしているのと、拡張もしやすかった。

インストールは以下のとおり。簡単。

npm install winston

最もシンプルな使い方としては以下のように、winston をそのまま使うやりかた

var winston = require('winston');

winston.silly('ばかばかしいこと');
winston.debug('かいはつのこと');
winston.verbose('どうでもいいこと');
winston.info('じょうほう');
winston.warn('やばい');
winston.error('すごくやばい');

実行すると以下の様な感じ

info: じょうほう
warn: やばい
error: すごくやばい

silly が定義してあるのは、npm のデフォルトのログレベルに準拠しているため。
ドキュメントみながら、そんなレベルあったっけな。と。

ファイルに出力したい場合は、これに transpoter を追加してやる必要がある。

var winston = require('winston');

winston.add(winston.transports.File, { filename: 'application.log' });
winston.silly('ばかばかしいこと');
winston.debug('かいはつのこと');
winston.verbose('どうでもいいこと');
winston.info('じょうほう');
winston.warn('やばい');
winston.error('すごくやばい');

{"level":"info","message":"じょうほう","timestamp":"2014-05-13T09:58:46.124Z"}
{"level":"warn","message":"やばい","timestamp":"2014-05-13T09:58:46.125Z"}
{"level":"error","message":"すごくやばい","timestamp":"2014-05-13T09:58:46.126Z"}

JSONで出力したくない場合は以下のようにする。

var winston = require('winston');

winston.add(winston.transports.File, { filename: 'application.log', json: false });
winston.silly('ばかばかしいこと');
winston.debug('かいはつのこと');
winston.verbose('どうでもいいこと');
winston.info('じょうほう');
winston.warn('やばい');
winston.error('すごくやばい');

2014-05-13T10:01:54.273Z - info: じょうほう
2014-05-13T10:01:54.274Z - warn: やばい
2014-05-13T10:01:54.275Z - error: すごくやばい

その他に、日付出さないようにする設定だとか、1ファイルあたりのサイズ・ファイルの上限などを
.add() の第2 引数を設定できたりする。
https://github.com/flatiron/winston/blob/master/docs/transports.md#file-transport

ただ、これだと、logger を別々にできないので以下のように別の
logger を定義することができたりする。

var logger = new (winston.Logger)({
  transports: [
    // 使う出力方法を transports で宣言する
    new (winston.transports.Console)({
      level: 'silly', // level は silly 以上
      colorize: true, // 色付き
      timestamp: true // 時間つき
    })
  ]
});

logger.silly('ばかばかしいこと');
logger.debug('とてもどうでもいいこと');
logger.verbose('どうでもいいこと');
logger.info('じょうほう');
logger.warn('やばい');
logger.error('すごくやばい');

スクリーンショット 2014-05-13 19.21.24

なお、メールでの送信は、
https://github.com/flatiron/winston/blob/master/docs/transports.md#mail-transport
が、公式のドキュメントに書かれているが、メッセージを全件送ってしまうので
大量にエラーが発生した時なんかは、少し困ったことになる可能性がある。

そこで、他の人が作った winston-maller がおすすめで、
こちらは、一定期間に来るメールの量を制限することができる。

Sails.js (0.9.x) で transpoter を追加したい場合は、
config/log.js を以下のように変更すれば良い。

var config = {
  level: 'info', // default log level
  adapters: {
    Mail: {
      module: 'winston-mailer',
      to: 'udon@bucyou.net',
      from: 'udon@bucyou.net'
    }
  }
};

adapters オブジェクト (なぜかドキュメントに書いていない) は、
キーに transporter オブジェクト名、module に require 時に必要な名前を指定できる。
https://github.com/machadogj/node-winston-mailer の場合は、

require('winston-mailer').Mail;

winston.add(winston.transports.Mail, options);

の用に使えます、とあるので、1行目にある Mail をキー名として使えば良い。