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, }, '.'); } });
速くなるのは当たり前だけど、予想より簡単だったのがうれしかった。