Benchmark: version object vs. eval

perldeltaには載っていなかったが,Perl 5.10.1でバージョンオブジェクトのAPIが変わった。古いAPIも残してあるものの新しいスタイルが強く奨励されるということだ。

# old, deprecated style (< 0.75)
use version; $VERSION = version->new('1.002003');
use version; $VERSION = qv('v1.2.3');

# new style
use version 0.77 (); $VERSION = version->parse('1.002003');
use version 0.77 (); $VERSION = version->delcare('v1.2.3');

実数形式のバージョン(Decimal Versions)にはparse()を,"v"が先行するv-string形式のバージョン(Dotted Decimal Versions)にはdelcare()を使えということらしい。

ところで,このバージョンオブジェクトは使う価値があるのだろうか。Perlの数値解析器はアンダースコアを理解できないので,今は,$VERSIONに代入したのち,eval()で数値に変換して$VERSIONに再代入するのが主流だ。

# CPAN Indexorに開発版であることを知らせるために
# アンダースコアを使う
$VERSION = '1.002_01';
# このままだと数値として比較できないため,
# eval()で数値に変換する。
$VERSION = eval $VERSION;

しかしこの工程に意味があるのは開発版リリースのみであり,リリース版ではeval()は時間の無駄である。しかしもしeval()よりもversion->parse/declareのほうが高速であるならば,eval()よりもversionを使ったほうが無駄もなく優れているといえる。

そこで,eval()とversion->parse()を比較してみた。

Script:

#!perl -w
use strict;
use Benchmark qw(:all);
use version 0.77 ();
cmpthese -1 => {
    version => sub{
        my $v = version->parse('0.89_01');
    },
    eval    => sub{
        my $v = eval '0.89_01';
    },
};

Result:

            Rate    eval version
eval     91022/s      --    -67%
version 275692/s    203%      --

見ての通り,versionのほうが3倍ほど高速だ。eval()をするくらいならversionの導入を検討してもいいかもしれない。

…と思ったが,アンダースコアを取り除くなら$VERSION =~ s/_//でも事足りるうえ,こちらはversionの8倍ほど速いので,機能のためならともかく,速度のためにversionを使うくらいならs/_//でいいことになる。

なお,バージョンのセットアップはひとつのパッケージにつき1回あるかないかという些細なことなので,いずれにせよ体感できるほど高速になるわけではない。