Accessorの比較は、App::Benchmark::Accessorsが有名ですよね。 しかし、NanoAで使われている、Class::Accessor::Lite等、比較されていないモジュールもあります。 Accessorとして使う分には十分な機能を持っているLiteを比較してみたかったので、いくつかのモジュールを選んでベンチマークを取ってみました。
選んだのは、Liteを含めて5つのモジュール。
- Class::Accessor::Fast
- Class::Accessor::Faster
- Class::Accessor::Lite
- Object::Tiny::RW
- Object::Simple
アクセサをあらかじめ定義しておき、ベンチマークの中でnewしてsetしてgetしてsetする、という事をやりました。 まずは結果を。
1
2
3
4
5
6
7
8
9
10
11
12
|
Benchmark: timing 100000 iterations of caf, cafr, cal, os, otrw...
caf: 4 wallclock secs ( 5.09 usr + 0.00 sys = 5.09 CPU) @ 19661.82/s (n=100000)
cafr: 5 wallclock secs ( 4.63 usr + 0.00 sys = 4.63 CPU) @ 21584.29/s (n=100000)
cal: 4 wallclock secs ( 3.51 usr + 0.00 sys = 3.51 CPU) @ 28490.03/s (n=100000)
os: 3 wallclock secs ( 3.71 usr + 0.00 sys = 3.71 CPU) @ 26932.40/s (n=100000)
otrw: 3 wallclock secs ( 3.67 usr + 0.00 sys = 3.67 CPU) @ 27277.69/s (n=100000)
Rate caf cafr os otrw cal
caf 19662/s -- -9% -27% -28% -31%
cafr 21584/s 10% -- -20% -21% -24%
os 26932/s 37% 25% -- -1% -5%
otrw 27278/s 39% 26% 1% -- -4%
cal 28490/s 45% 32% 6% 4% --
|
上位3モジュールは僅差ですが、Class::Accessor::Liteが最も速かったです。 Liteは、use baseを使った継承ではなく、モジュールを使うようにして定義します。 Class::AccessorやFastに慣れた人は戸惑うかもしれません。 また、継承を使わないため、newメソッドなどは別で実装する必要があります。 スピードは次点ですが、Object::Tiny::RWはアクセサの定義が簡単でした。 useする時に、アクセサをインポートするように宣言すればよいだけで、アクセサの作成を意識しません。 ソースは読んでいないので仕組みはわかりませんが、newメソッドも使えるので便利です。 ただ、名前が惜しいです。 Object::TinyがReadOnlyのアクセサを作成するモジュールなのでしょうがないのでしょうが、付け足した感が満載です。 Object::Simpleは、他の4つのモジュールと違う動作をする箇所があります。 他のモジュールはsetの時に、その値を返しますが、Object::Simpleはオブジェクトそのものを返します。 慣れもあるのでしょうが、この挙動は不思議に感じました。 また、メソッド作成で複数のアクセサが指定出来ないのも不便に感じます。 まあ、このモジュールは、アクセサじゃなくアトリビュートという扱いっぽいので、狙うところが違うのかもしれません。 独断と偏見でまとめると、NanoAで使うならClass::Accessor::Lite、それ以外で使うならObject::Tiny::RWが良いと思いました。 ベンチマークのソースは以下のとおりです。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
|
# utf8
use 5.8.1;
use strict;
use warnings;
use utf8;
package Bench::CAF;
use base qw(Class::Accessor::Fast);
__PACKAGE__->mk_accessors(qw(foo hoge));
package Bench::CAFr;
use base qw(Class::Accessor::Faster);
__PACKAGE__->mk_accessors(qw(foo hoge));
package Bench::CAL;
use Class::Accessor::Lite;
Class::Accessor::Lite->mk_accessors(qw(foo hoge));
sub new { bless {}, $_[0] }
package Bench::OTRW;
use Object::Tiny::RW qw(foo hoge);
package Bench::OS;
use base qw/Object::Simple/;
__PACKAGE__->attr("foo");
__PACKAGE__->attr("hoge");
package main;
use Benchmark qw(:all);
use Perl6::Say;
# use Data::Dumper;
sub confirm {
# say @_;
}
cmpthese(timethese( 100_000,
{
# Class::Accessor::Fast
caf => sub {
my $ac = Bench::CAF->new;
confirm($ac->foo("bar"));
confirm($ac->foo);
confirm($ac->hoge("fuga"));
},
# Class::Accessor::Faster
cafr => sub {
my $ac = Bench::CAFr->new;
confirm($ac->foo("bar"));
confirm($ac->foo);
confirm($ac->hoge("fuga"));
},
# Class::Accessor::Lite
cal => sub {
my $ac = Bench::CAL->new;
confirm($ac->foo("bar"));
confirm($ac->foo);
confirm($ac->hoge("fuga"));
},
# Object::Tiny
otrw => sub {
my $ac = Bench::OTRW->new;
confirm($ac->foo("bar"));
confirm($ac->foo);
confirm($ac->hoge("fuga"));
},
# Object::Simple
os => sub {
my $ac = Bench::OS->new;
confirm($ac->foo("bar"));
confirm($ac->foo);
confirm($ac->hoge("fuga"));
},
}));
|
この記事を書きながら、何度か実行しましたが、上位3モジュールの順位は入れ替わることもありました。