MHI/2.1
(2009.01.03)
$ [Perl] リファレンス返しのパフォーマンス

呼び出し元へ値を返す場合、戻り値で返す他、 参照渡しで引数で渡された変数へ関数側が値を返す方法がある。
ここで同じことをするのに、その 「リファレンスの書き方」で、どれだけ速度差があるか計測してみた。
use strict;
use warnings;
use Benchmark;
my $cnt1=0;
my $cnt2=0;
my $cnt3=0;
timethese(1000000,
{
'Perl-4 Reference' => sub{ bar1($cnt1);} ,
'Perl-5 Reference[1]' => sub{ bar2(\$cnt2);} ,
'Perl-5 Reference[2]' => sub{ bar3(\$cnt3);} ,
}
);
print "cnt1=$cnt1\n";
print "cnt2=$cnt2\n";
print "cnt3=$cnt3\n";
sub bar1{
$_[0]++;
}
sub bar2{
${$_[0]}++;
}
sub bar3{
my $p=shift;
${$p}++;
}
- bar3 は標準的な参照渡し。\ 演算子で参照を渡している。
- bar2 は shift を使わず、直接 ${$_[0]} を使って、速度を稼ごうとている。
- bar1 は Perl 4 の参照渡し。@_ を $_[…] のままで変更すると、 呼び出し元の内容にも変更が反映される。
結果発表:
Benchmark: timing 1000000 iterations of Perl-4 Reference, Perl-5 Reference[1], Perl-5 Reference[2]... Perl-4 Reference: 1 wallclock secs ( 0.42 usr + 0.00 sys = 0.42 CPU) @ 2375296.91/s (n=1000000) Perl-5 Reference[1]: 1 wallclock secs ( 0.75 usr + 0.00 sys = 0.75 CPU) @ 1331557.92/s (n=1000000) Perl-5 Reference[2]: 1 wallclock secs ( 1.34 usr + 0.00 sys = 1.34 CPU) @ 745712.16/s (n=1000000) cnt1=1000000 cnt2=1000000 cnt3=1000000
一番最後に cntN の値を表示させることで、 呼び出し側の変数が修正されていることを確認している。
予想どおり、最も演算子の少ない Perl-4 Reference が速いことが分かった。
残念ながら、Perl-4 Reference は wifky では全く使っていない。もったいない話だ。 (互換性の都合、今から変更するのは難しい)
$ 思えば、充実してそうで、してなかったような年末年始であったのことよ

(2009.01.02)
$ [Python3] 日本語ファイル名が os.listdir に出ない件について

Python 3.0 で os.walk を使うと、日本語ファイル名が弾かれてしまうみたいだ…。
Note that when os.listdir() returns a list of strings, filenames that cannot be decoded properly are omitted rather than raising UnicodeError.
os.listdir() が文字列のリストを返す時、unicode化できないファイル名は UnicodeError を投げるのではなく、適切に?省略されてしまう。
あー
$ はつもうで

$ [Perl] Code in wifky.pl: 関数の横取り

wifky では機能拡張のため、様々なフックを用意している。
例を上げると、a=xxxx というCGIパラメータに対して、 特定の機能を立ち上げるために
%::action_plugin = (
'index' => sub{ &do_index('recent','rindex','-i','-a','-l'); },
;
'signout' => \&action_signout ,
);
といった関数テーブルを用意し、
$::action_plugin{ $::form{a} }->();
と呼び出している。 このように同じような関数が多数ある場合については、 ハッシュによる関数テーブルが有効である。
だが、単品の1機能に対して、いちいちフックを用意していてはキリがない。
そこで、wifky のプラグインでは、シンボルテーブルを書換えるという方法で 本体の関数を差し替えるといったことを多用している。
関数の差し替えについて、
Perlクックブック〈VOLUME1〉(p.496)では次のような方法が紹介されている。
undef &grow; *grow = \&expand; grow();
だが、この方法では旧growが undef によってシンボルテーブルから消去されてしまうため、 新grow (expand) の中から、旧growの機能を利用することができない。
こういう場合、型グロブで旧関数と新関数を入れ変えてしまえばよい。
#!/usr/bin/perl
use strict;
use warnings;
(*grow,*org_grow)=(*new_grow,*grow);
grow();
sub grow{
print "original-grow\n";
}
sub new_grow{
print "before-grow\n";
org_grow();
print "after-grow\n";
}
$ perl foo.pl before-grow original-grow after-grow
- 旧関数を undef せずに、型グロブに関数リファレンスを直接代入すると、 Subroutine main::grow redefined at foo.plという二重定義の警告が出る。
- 型グロブに型グロブを代入する場合、旧関数が定義済みでも警告が出ない。
この方法は、本体側でわざわざフックを用意しなくてもよいため、 本体を極限までコンパクトにしたいという wifky では極めて有効だった。
(なお、一箇所だけ単品フックがある($::hook_submit)があるが、 この方法を知らなかった頃に設置したもので、 現在は互換性のためだけに残していることを注記しておく)