読者です 読者をやめる 読者になる 読者になる

ぱいぱいにっき

Pythonが好きすぎるけれど、今からPerlを好きになりますにっき

MongoDB.pmをMouseに書き換えてみただけ

あ、はい、マコピーです。最近麻雀にハマっています。

そんなわけなんですけれど、最近MongoDBを僕が関わっている案件に導入しまして、こいつで解析をやろうと思ったわけなんですが、PerlでMongoDBを扱うためのモジュールがいくつかあって、

MongoDB - A Mongo Driver for Perl - metacpan.org
公式のやつ
AnyMongo - Asynchronous non-blocking MongoDB driver for AnyEvent applications - metacpan.org
AnyEvent使うやつ
MongoDB::Async - Asynchronous Mongo Driver for Perl - metacpan.org
Coroとか使うやつ

とまあいろいろあって、参考にしたのは
PerlでMongoDBを扱う際のモジュールなどのまとめ - NAVER まとめ
なんですけれど、他にもいろいろORMっぽいやつとかあるみたいです。

で、大抵そのORMたちは公式のMongoDB.pm使ってるんですが、まあこいつの難点としてMoose依存だったりDateTime依存だったりするわけですね〜。

Perlあまり知らない人に説明するとMoosePerlOOPをやるためのライブラリで結構モダンに書けるのですが、重たくて今はオワコンって言われてます。代替モジュールとしては@さんのMouseとか海外で最近良く使われてるMooがあります。
ここの下の方にそんなMooseの代替モジュールがまとめられています。
僕はMouse派です。gfxさんのマジックスパイスがかかっているのでなんでこんなに速いのかはわかりませんが……。

で、DateTime。一昔前まではPerlで日付を扱うにはこれしかないっていうモジュールだったのですが、今はコアモジュールのTime::Pieceを使うのが普通らしいです。それに関しては昨日の記事を。

さて、MongoDB.pmを使おうとした僕ですが他で使ってもない重たいMooseとかDateTimeを入れたくなかったので、こいつをどうにか書き換えてMouseとTime::Piece使うように出来ないかと考えたわけです。まあ半分遊びみたいなもんですが……。
Time::Pieceの方は今はDateTime::Tinyっていう速いモジュールも使えるよってなってたのでとりあえずこっちは後回しにして、MooseからMouseに書き換えてみました。

元ソースは
mongodb/mongo-perl-driver · GitHub

落としてきておもむろに

ag -al 'Moose' | perl -pi -e 's/Moose/Mouse/g'

とするだけのゆとり一斉置換をしてみました。agってのはThe Silver Searcherっていう無茶苦茶早くファイルの中の文字を検索してくるやつです。お試しあれ。

で、その後おもむろに

perl Makefile.PL
make
make test

してテストを走らせてちゃんと全部動くか試行錯誤していくわけですが、とりあえずテストが全部通るまでやったことは、

1. どこかのテストでAny::Moose使ってて「!?」ってなったのでMouseに書き換えた
2. MongoDB::ConnectionでMongoDB::MongoClientに移譲しているところで

has '_client' => (
    isa         => 'MongoDB::MongoClient', 
    is          => 'ro',
    handles     => [ grep { $_ !~ /^(meta|new)$/ } 
                     map { $_->name } Class::MOP::Class->initialize( 'MongoDB::MongoClient' )->get_all_methods 
                   ]
);

っていう風にMongoDB::MongoClientのメソッド全部取ってきて突っ込むっていう感じのことをやっていて、これをMouse::Meta::Classで以下のように書き換えた。

has '_client' => (
    isa         => 'MongoDB::MongoClient',
    is          => 'ro',
    handles     =>  [
        grep { $_ !~ /^(meta|new|[A-Z]+)$/ } 
        map { $_->name } Mouse::Meta::Class->initialize( 'MongoDB::MongoClient' )->get_all_methods 
    ],
);

ちなみにすぐ上にあるuse boolean;も取り除かないとMouseでdelegateするときに怒られて死にます。そのあたりMouseのほうが厳密なんでしょう。
あと移譲しないメソッドに[A-Z]+、つまり大文字だけのメソッドを追加したのはAUTOLOADを丸投げされてテストが通らなくなったため。AUTOLOADだけ追加してもいいけれど大文字基本的にアウトだろ……って思ってこうした感じです。あまり根拠はないのでツッコミ歓迎。

これでテスト動くようになったのでベンチ取ってみました。
コードは以下のような。

insertしてfindするっていうのを単純にやる感じです。
どっちも同じ「MongoDB」なのでuseした時点で%INCに入っちゃってno MongoDBとかやってもダメっぽいので、コメントアウトしながらかたっぽずつ実行。%INCいじれば大丈夫なんでしょうか。試していません。

結果。

[- o -] $ perl bench_mongodb.pl 
                Rate Mouse MongoDB
Mouse MongoDB 1149/s            --
[- o -] $ perl bench_mongodb.pl
               Rate Moose MongoDB
Moose MongoDB 978/s            --

2割弱ほど早くなりましたねー。メモリ消費量とかは測ってないです。

とまあとりあえず書き換えてみましたけれどこれを実戦投入できるかどうかってのはアレですが、こんなかんじで比較的簡単にMouseに書き換えられるんですねー。興味深い。
Time::Piece化はスクラッチで書いたほうがいいかなーって思ったのでやるかどうかわかりません〜。
あとPurePerlのMongoDBプロトコルの実装見つけたので試したい〜。


あ、ちなみに「[- o -]」っていうのはハコフグです。