Storable モジュールの lock_store は遅いが lock_retrieve は速い

MT4i Version 3.0 のキャッシュ機能搭載に当たり、「何となく速い気がする」という理由で Storable モジュールを使用していました。

高速化を考えた時、改めて「単純な Open-Close と比べてどうなの?ホントに速いの?」と疑問に思ったので、Benchmark で比較してみました。

まずは書き込みを比較。

#!/usr/local/bin/perl
use strict;
use warnings;

use Benchmark;
use Storable qw(lock_store lock_retrieve);

open (IN, 'storable_test.html');
my @data = <IN>;
close (IN);
my $data = join('', @data);

my $ocache_file = 'storable_test.ocache';
my $scache_file = 'storable_test.scache';

my $count = 50_000;

timethese($count, {

    '00.OpenClose' => sub {
        # write
        if (!-e $ocache_file) {
            open(OUT,"> $ocache_file") or die "Can't open ".$ocache_file." : $!";
        } else {
            open(OUT,"+< $ocache_file") or die "Can't open ".$ocache_file." : $!";
        }
        flock(OUT, 2) or die "Can't flock  : $!";
        seek(OUT, 0, 0) or die "Can't seek  : $!";
        print OUT $data or die "Can't print : $!";
        truncate(OUT, tell(OUT)) or die "Can't truncate : $!";
        close(OUT);
    },
    '01.Storable' => sub {
        # store
        eval { lock_store(\$data, $scache_file); };
        die 'store cache failed: '.$@ if($@);
    },
});

"storable_test.html" は、一般的な HTML 文書だと思ってください。実行結果は以下の通り。

%perl storable_test.pl
Benchmark: timing 50000 iterations of 00.OpenClose, 01.Storable...
00.OpenClose:  2 wallclock secs ( 0.73 usr +  0.78 sys =  1.51 CPU) @ 33160.62/s (n=50000)
01.Storable: 10 wallclock secs ( 4.83 usr +  1.89 sys =  6.72 CPU) @ 7441.86/s (n=50000)

単純な Open-Close の方がべらぼーに速いです。これは Storable ピンチか?!と思いつつ次は読み込み。

#!/usr/local/bin/perl
use strict;
use warnings;

use Benchmark;
use Storable qw(lock_store lock_retrieve);

my $ocache_file = 'storable_test.ocache';
my $scache_file = 'storable_test.scache';

my $count = 20_000;

timethese($count, {

    '00.OpenClose' => sub {
        open (IN, $ocache_file);
        my @cdata = <IN>;
        close (IN);
        my $data = join('', @cdata);
    },
    '01.Storable' => sub {
        # retrieve
        my $cdata;
        eval { $cdata = lock_retrieve($scache_file); };
        die 'retrieve cache failed: '.$@ if($@);
        my $data = $$cdata;
    },
});

これの実行結果は以下の通り。

%perl read_retrieve_test.pl
Benchmark: timing 20000 iterations of 00.OpenClose, 01.Storable...
00.OpenClose:  9 wallclock secs ( 6.99 usr +  0.23 sys =  7.23 CPU) @ 2767.57/s (n=20000)
01.Storable:  1 wallclock secs ( 0.75 usr +  0.23 sys =  0.98 CPU) @ 20317.46/s (n=20000)

今度は Storable の方が圧倒的に速いです。Open-Close の方は flock し忘れているにも関わらず。

また、読み書き1セットで Benchmark を取ると、Storable の方が遅いのですが、Storable は書き込みの高速化を捨てて付随情報を同時に書き込んでやることにより、読み込み速度を上げています。今回の用途であるキャッシュ機能を考えれば、書き込みよりも読み込みの方が多く発生し、求められるのは読み込みのスピードです。ということで、今のまま Storable を使用することで問題は無さそうです。