Moose触ってみるよ!

触るんかい!

と、言うことで使ってないうちからあれこれ言うのは愚の骨頂ってことでちょっち実務で使ってみるね!少しだけだけど。

でいつもの事ながら組み込むとなればやはり気になるのがパフォーマンス。もう散々書かれてるけど自分で試さないと気がすまないのでベンチ。

#!/usr/local/bin/perl -w

package UseMoose;

use Moose;

has userid => ( is => 'rw' );
has agent  => ( is => 'rw', isa => 'Defined' );

sub BUILD {
    my $self = shift;
    $self->userid( 90);
    $self->userid( $self->agent->user_id ) unless $self->agent->is_non_mobile;
}

package NoMoose;
use strict;
use warnings;
use base qw/Class::Accessor::Fast/;
use Carp qw/croak/;

__PACKAGE__->mk_accessors(qw/
    userid
    agent
/);

sub new {
    my $self = shift->SUPER::new({ @_});
    $self->agent or croak 'agent not found';
    $self->userid( 90 );
    $self->userid( $self->agent->user_id ) unless $self->agent->is_non_mobile;
    return $self;
}

package main;

use HTTP::MobileAgent;
my $agent = HTTP::MobileAgent->new('');

use Benchmark qw(cmpthese timethese :hireswallclock);

cmpthese(1000,{
    use_moose => sub {
        my $a = UseMoose->new( agent => $agent );
        $a->userid('hoge');
    },
    no_moose    => sub {
        my $b = NoMoose->new( agent => $agent );
        $b->userid('hoge');
    },
});

UseMooseクラスとNoMooseクラス。

処理内容はだいたい同じ。useridとagentというアクセサを用意してagentが渡されていなければdieするってだけのもの。

UseMooseはMooseを使ってて、NoMooseはClass::Accessor::Fastやcroakなどを使ってる感じ。

さぁ、結果は?

             Rate use_moose  no_moose
use_moose   427/s        --      -99%
no_moose  62500/s    14550%        --

はいはい。激遅激遅。

しかしこれはMoose使いのみなさんなら周知の事実です。遅くて当然なんです。必然なんです。

http://d.hatena.ne.jp/tokuhirom/20080504/1209891158

ってことでmake_immutableしとかないと、遅すぎてとてもじゃないけど実務ではまったく全然極めつけのスーパーウルトラスイーツ死んだ級に使えません。もちろん何度も呼ばれないような一発系の処理では有りかもしれませんが・・・。

make_immutableしてベンチ取り直してみます。

package UseMoose;

use Moose;

has userid => ( is => 'rw' );
has agent  => ( is => 'rw', isa => 'Defined' );

sub BUILD {
    my $self = shift;
    $self->userid( 90);
    $self->userid( $self->agent->user_id ) unless $self->agent->is_non_mobile;
}

__PACKAGE__->meta->make_immutable; # ここがポイント!
# for windows
             Rate use_moose  no_moose
use_moose 32258/s        --      -48%
no_moose  62500/s       94%        --

# for linux
             Rate use_moose  no_moose
use_moose 10000/s        --      -20%
no_moose  12500/s       25%        --

これならば許容範囲です。まーRole使ったりもっと複雑なことをすると色々変わってくるとは思いますが多分オッケーでしょう。

make_immutableの戻り値には1が返ってくるようなので1;の代わりとして使うといい感じですかねー。多分それを意図しての設計なのかな?

ただ、「__PACKAGE__->meta->make_immutable」とか長いしタイプが面倒なので、MyMooseみたいなのを作って・・・

package MyMoose;

use Moose;

sub import {
    my $class = scalar caller;
    Moose->import( { into => $class } );
    $class->meta->add_method($_[1] || 'eom',sub { $class->meta->make_immutable } );
}

1;
package UseMoose;

use MyMoose; # MyMooseにして

has userid => ( is => 'rw' );
has agent  => ( is => 'rw', isa => 'Defined' );

sub BUILD {
    my $self = shift;
    $self->userid( 90);
    $self->userid( $self->agent->user_id ) unless $self->agent->is_non_mobile;
}

eom; # end of moduleの略

って感じにしてみました。この辺ってMoose使いの人たちはどうしてるのかな?

アプリ側でMooseを使用してるクラス名を保持しておいて、

$_->meta->is_immutable or $_->meta->make_immutable for @class;

みたいな感じで最後に一気にmake_immutableしたりとか?でもそれだとpmファイル単位での解決じゃないから微妙か。

さてさてそんなこんなで少しだけ組み込んでまする。メンテ担当者が困らない程度に・・・ね。# ってMooseの時点でまいっちんぐか><