Perlのinstanceof演算子のベストプラクティスは何か

instanceof演算子 - Islands in the byte stream

確かに遅いですよねぇ。

ただ、一応ベンチとってみたところ、blessed使うのが一番早かったです。

以下、$xがオブジェクトの場合のベンチ。

use Benchmark qw(cmpthese timethese :hireswallclock);

my $x = bless {} , 'Foo';
my $class = 'Foo';

cmpthese(100000,{
    ref_eval => sub {
        if ( ref($x) && eval { $x->isa($class) } ) {}
    },
    scalar_util    => sub {
        if ( Scalar::Util::blessed($x) && $x->isa($class) ) {}
    },
    param_util    => sub {
        if ( Params::Util::_INSTANCE($x, $class) ) {}
    },
});

__END__

                Rate  param_util    ref_eval scalar_util         ref
param_util  377358/s          --        -23%        -47%        -53%
ref_eval    492611/s         31%          --        -31%        -38%
scalar_util 709220/s         88%         44%          --        -11%

以下、$xがリファレンスの場合のベンチ。

use Benchmark qw(cmpthese timethese :hireswallclock);

my $x = {};
my $class = 'Foo';

cmpthese(100000,{
    ref_eval => sub {
        if ( ref($x) && eval { $x->isa($class) } ) {}
    },
    scalar_util    => sub {
        if ( Scalar::Util::blessed($x) && $x->isa($class) ) {}
    },
    param_util    => sub {
        if ( Params::Util::_INSTANCE($x, $class) ) {}
    },
});

__END__

                 Rate   ref_eval param_util scala_util        ref
ref_eval     156006/s         --       -78%       -90%       -93%
param_util   714286/s       358%         --       -56%       -66%
scalar_util 1612903/s       934%       126%         --       -24%

結局evalで括ってるのが遅い原因なんですよねぇ。ためしにeval外してベンチとってみたらblessedよりもかなり早くなりました。

でもeval外してしまうと$xがリファレンスだった場合に死亡してしまうので悩ましいところです。


一応、特殊なオブジェクトを無視するということにすれば高速にチェックすることも可能です。

特殊なオブジェクトとはつまり、HASHやARRAYといったリファレンス名と同名のオブジェクトのことです。

これらを無視するのであれば下記のようにevalを外せるので高速にinstanceofできます。

our $CACHE = { map { $_ => 1 } qw/HASH SCALAR ARRAY GLOB CODE REF/,'' };

if ( !$CACHE->{ref $x} && $x->isa($class) ) {
}

さてこれを追加した上でベンチをとりなおすと。

# $xがオブジェクトの場合のベンチ。

                Rate param_util   ref_eval scala_util        ref
param_util  375940/s         --       -24%       -47%       -65%
ref_eval    492611/s        31%         --       -30%       -54%
scala_util  704225/s        87%        43%         --       -35%
ref        1075269/s       186%       118%        53%         --

# $xがリファレンスの場合のベンチ。

                Rate   ref_eval param_util scala_util        ref
ref_eval    156006/s         --       -78%       -90%       -93%
param_util  714286/s       358%         --       -56%       -66%
scala_util 1612903/s       934%       126%         --       -24%
ref        2127660/s      1264%       198%        32%         --

となりました。$xがリファレンスの場合が爆速ですね。ハッシュのチェックの時点でifを抜けるので当然っちゃ当然ですが。

だたし先程も言ったとおり$xが

$x = bless {} , 'HASH';

こーゆーのだと偽になってしまうので要注意です。ってかそんな名前のオブジェクト作ってんじゃねーよ、このボケが!って所でしょうか><