てきとうなメモ

本の感想とか技術メモとか

Class::Structのベンチマーク

Class::Structの速度とかメモリ効率とかをちょっと調べてみた.比較対象はClass::Accessor.
Class::Structはstruct関数で配列のリファレンスを渡すと配列をblessし,ハッシュならハッシュをblessする.

package ClassPerson;
use base qw(Class::Accessor);
ClassPerson->mk_accessors(qw(name age salary));

package StructArrayPerson;
use Class::Struct;
struct(StructArrayPerson => [name => '$', age => '$', salary => '$']);

package StructHashPerson;
use Class::Struct;
struct(StructHashPerson => {name => '$', age => '$', salary => '$'});

package main;
use Benchmark qw(:all);
use Devel::Size qw(size total_size);

sub mkobj1 {
  return ClassPerson->new({name => "John", age => 20, salary => 200000});
}

sub mkobj2 {
  return StructArrayPerson->new({name => "John", age => 20, salary => 200000});
}

sub mkobj3 {
  return StructHashPerson->new({name => "John", age => 20, salary => 200000});
}

my $obj1 = mkobj1;
my $obj2 = mkobj2;
my $obj3 = mkobj3;

sub getobj1 {
  return $obj1->name;
}

sub getobj2 {
  return $obj2->name;
}

sub getobj3 {
  return $obj3->name;
}

sub setobj1 {
  $obj1->name("Mike");
}

sub setobj2 {
  $obj2->name("Mike");
}

sub setobj3 {
  $obj3->name("Mike");
}

print "constructor\n";
cmpthese(100000, {"Class::Accessor" => \&mkobj1, "Class::Struct(Array)" => \&mkobj2, "Class::Struct(Hash)" => \&mkobj3});
print "\ngetter\n";
cmpthese(1000000, {"Class::Accessor" => \&getobj1, "Class::Struct(Array)" => \&getobj2, "Class::Struct(Hash)" => \&getobj3});
print "\nsetter\n";
cmpthese(1000000, {"Class::Accessor" => \&setobj1, "Class::Struct(Array)" => \&setobj2, "Class::Struct(Hash)" => \&setobj3});

print "Class::Accessor: ", total_size($obj1), "\n";
print "Class::Struct(Array): ", total_size($obj2), "\n";
print "Class::Struct(Hash): ", total_size($obj3), "\n";

こんな感じで速度とメモリサイズを比較する.速度の比較にはBenchmark,メモリサイズの比較にはDevel::Sizeを用いた.

で結果は以下の通り.

constructor
                        Rate Class::Struct(Hash) Class::Struct(Array) Class::Accessor
Class::Struct(Hash)  55249/s                  --                  -6%            -20%
Class::Struct(Array) 58824/s                  6%                   --            -15%
Class::Accessor      69444/s                 26%                  18%              --

getter
                         Rate Class::Accessor Class::Struct(Hash) Class::Struct(Array)
Class::Accessor      234742/s              --                -45%                 -49%
Class::Struct(Hash)  423729/s             81%                  --                  -8%
Class::Struct(Array) 460829/s             96%                  9%                   --

setter
                         Rate Class::Accessor Class::Struct(Hash) Class::Struct(Array)
Class::Accessor      204499/s              --                -46%                 -49%
Class::Struct(Hash)  375940/s             84%                  --                  -7%
Class::Struct(Array) 404858/s             98%                  8%                   --
Class::Accessor: 232
Class::Struct(Array): 121
Class::Struct(Hash): 278

newするのは遅いのだがsetterとgetterはClass::Structの配列形式のが一番速い.あと,メモリのサイズはやっぱり配列を使っているだけ少なくすんでいる.速度に関してはClass::Accessorはちょっと遅いという話も聞いていて,Class::Accessor::Fastというモジュールもある.でもやはり配列でオブジェクトを生成するとメモリ効率はよくなるという点は変わらないかなと思う.