Yet another alias module: Scalar::Alias

Lexical::Typesを見て型つきレキシカル宣言の威力を知り,試してみたくなったので一つモジュールを書いてみた。

この型つきレキシカル宣言はうまく使うといろいろ面白いことができそうだ。しかもオーバーヘッドがコンパイル時のみというのがうれしい。

#!perl -w
use strict;
use Scalar::Alias;

sub inc{
  my alias $x = shift;
  $x++;
  return;
}

my $i = 0;
inc($i);
print $i, "\n"; # => 1
__END__

エイリアスを実現するモジュールは既に数多くあるが,このモジュールは高速であることが特徴となっている。これは,PL_peeppハックによってコンパイル済みの構文木を直接変更しており*1,レキシカル変数の参照と代入を独自のopcodeに差し替えていることによる。したがって,コンパイルに余計な時間とメモリを使用する代わり,実行時には余計なコストは掛からない。むしろ,値のコピーを行わない分通常の代入文よりも高速でさえある。

なお,速度については,通常の代入と比較してみた(perl 5.10.0 linux, multi-threaded, -DDEBUGGING)。

For integer
          Rate assign  alias
assign 49321/s     --    -6%
alias  52609/s     7%     --
For string
          Rate assign  alias
assign 41754/s     --   -22%
alias  53593/s    28%     --
For object reference
          Rate assign  alias
assign 42708/s     --   -20%
alias  53095/s    24%     --

対象となる値が整数の場合はあまり変わらないが,それ以外の場合は25%程度高速であるようだ。ただし,この結果はPerlのバイナリによってもかなり差がある。別のバイナリ(5.8.8 linux, multi-threaded)ではこれより差が大きかったが,ここでは差が小さいほうを記しておく。

ベンチマークスクリプト

#!perl -w
use strict;
use Benchmark qw(:all);
use Scalar::Alias;
print "For integer\n";
my @integers = ((42) x 100);
cmpthese -1 => {
	alias => sub{
		for my $i(@integers){
			my alias $x = $i;
		}
	},
	assign => sub{
		for my $i(@integers){
			my $x = $i;
		}
	},
};
print "For string\n";
my @strings = (('foo') x 100);
cmpthese -1 => {
	alias => sub{
		for my $i(@strings){
			my alias $x = $i;
		}
	},
	assign => sub{
		for my $i(@strings){
			my $x = $i;
		}
	},
};
print "For object reference\n";
my @refs = ((bless{}) x 100);
cmpthese -1 => {
	alias => sub{
		for my $i(@refs){
			my alias $x = $i;
		}
	},
	assign => sub{
		for my $i(@refs){
			my $x = $i;
		}
	},
};
__END__

*1:Acme::Perl::VMを実装した時の知識が活きてきた。