Subversion のワーキングコピー上の File::Find を速くする
Perl の File::Find で簡単な、たとえば JavaScript のファイルを列挙する処理を書いたとする。
find(sub { print "$File::Find::name\n" if m/\.js$/; }, '.');
いつもこんな風にすませていたんだけど、これを Subversion レポジトリのワーキングコピー上で走らせると普通より遅い。ワーキングコピー上のすべてのフォルダには .svn という Subversion のメタデータを保存するフォルダがあり、そもそもたどるべき木が大きいので当たり前だ。
たとえば Google Closure Library の場合 svn checkout したものが150MB, svn export したものが69MBになる。
各地の .svn 以下の部分木を無視すればいいので、いつか枝刈りつきの File::Find を書くか探すかしようとしていたんだけど、preprocess オプションをつけるとそれができることがわかった。
find({
wanted => sub {
print "$File::Find::name\n" if m/\.js$/;
},
preprocess => sub {
grep { $_ ne '.svn' } @_;
},
'.'
);
ベンチマークをとったらちゃんと速くなっていた。数字 (4倍) はレポジトリに左右されると思う。
% svn info
Path: .
URL: http://closure-library.googlecode.com/svn/trunk
Repository Root: http://closure-library.googlecode.com/svn
Repository UUID: 0b95b8e8-c90f-11de-9d4f-f947ee5921c8
Revision: 8
Node Kind: directory
Schedule: normal
Last Changed Author: dtbentley
Last Changed Rev: 8
Last Changed Date: 2009-11-13 13:51:43 +0900 (金, 13 11 2009)
% perl ~/find.pl
...
Rate plain preprocess
plain 5.94/s -- -71%
preprocess 20.4/s 243% --
%
ベンチマークに使った find.pl はこんな感じです。
use strict;
use warnings;
use File::Find;
use Benchmark;
sub print_if_javascript {
print "$File::Find::name\n" if m/\.js$/;
}
Benchmark::cmpthese(-1, {
preprocess => sub {
find({
wanted => \&print_if_javascript,
preprocess => sub {
grep { $_ ne '.svn' } @_;
}
}, '.');
},
plain => sub {
find({
wanted => \&print_if_javascript,
}, '.');
}
});
速くなるのは当たり前だけど、予想より簡単だったのがうれしかった。