Subversion のワーキングコピー上の File::Find を速くする

2009-11-16 03:13

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,
        }, '.');
    }
});

速くなるのは当たり前だけど、予想より簡単だったのがうれしかった。

Leave a Reply