編集(管理者用) | 編集 | 差分 | 新規作成 | 一覧 | RSS | 表紙 | 検索 | 更新履歴

&$blosxom::entries

blosxomの記事ファイルを検索するサブルーチンを書き換えてみる試み。

オリジナルの &$entries サブルーチンは、インデントが深くなっていたり、制御構造の and を多用していたりで読みにくかったので、同じ処理をしつつもう少し見やすいように書き換えてみました。

ソース

多分同じ処理になっていると思うのですが、保証はできません。

blosxomプラグインの、entriesサブルーチンの形式で書いてます。

sub entries {
    return sub {
        package blosxom;
        my (%files, %indexes, %others);
        find(
            sub {
                # $depthより深ければ復帰
                my $curr_depth = $File::Find::dir =~ tr[/][];
                return if ($depth && $curr_depth > $depth);

                # 現在の時刻、ファイル名、更新時刻の設定
                my $time = time;
                my $name = $File::Find::name;
                my $mtime = stat($name)->mtime;

                # ディレクトリなら復帰
                return if (-d $name);
                # 読めないなら復帰
                return unless (-r $name);

                # ファイル名のマッチ
                my ($path, $fn) =
                    $name =~ m{^$datadir/(?:(.*)/)?(.+)\.$file_extension$};
                $path ||='';

                # パターンにマッチしていないか、indexか、
                # .で始まるファイル名なら、%otherに追加して復帰
                if (!$fn || $fn eq 'index' || $fn =~ /^\./) {
                    $others{$name} = $mtime;
                    return;
                }

                # 未来の記事だったら復帰 (許可していない場合)
                return if (!$show_future_entries && $mtime > $time);

                # %filesに追加
                $files{$name} = $mtime;

                # 静的表示の処理 (%indexesに追加)
                my $check_index = "$static_dir/$path/index.$static_flavours[0]";
                return unless (
                    param('-all')
                    || !-f $check_index
                    || stat($check_index)->mtime < $mtime
                );
                $indexes{$path} = 1;
                my $d = join('/', (nice_date($mtime))[5,2,3]);
                $indexes{$d} = $d;
                $static_entries and $indexes{($path ? "$path/" : '') . "$fn.$file_extension"} = 1;
            }, $datadir
        );
        return (\%files, \%indexes, \%others);
    };
}

元のサブルーチンと比較

ベンチマークを取るために以下のようなスクリプトを作成。entries0 がオリジナルの、entries1が今回書き換えたサブルーチンです。

package blosxom;
use strict;
use CGI qw/:standard :netscape/;
use File::Find;
use File::stat;
use Time::localtime;
use Benchmark;

our $datadir = "/var/apache/htdocs/blosxom2/entries";
our $depth = 0;
our $show_future_entries = 0;
our $file_extension = "txt";
our $static_entries = 0;
our $static_dir = "/var/apache/htdocs/blosxom2/statics";
our @static_flavours = qw/html rss/;
our %month2num = (nil=>'00', Jan=>'01', Feb=>'02', Mar=>'03', Apr=>'04', May=>'05', Jun=>'06', Jul=>'07', Aug=>'08', Sep=>'09', Oct=>'10', Nov=>'11', Dec=>'12');

timethese(200, {
    'test0' => '&entries0;',
    'test1' => '&entries1;',
});

sub entries0 {
    package blosxom;
    my(%files, %indexes, %others);
    find(
      sub {
        my $d; 
        my $curr_depth = $File::Find::dir =~ tr[/][]; 
        return if $depth and $curr_depth > $depth; 

        if ( 
          # a match
          $File::Find::name =~ m!^$datadir/(?:(.*)/)?(.+)\.$file_extension$!
          # not an index, .file, and is readable
          and $2 ne 'index' and $2 !~ /^\./ and (-r $File::Find::name)
        ) {

          # to show or not to show future entries
          ( 
            $show_future_entries
            or stat($File::Find::name)->mtime < time 
          )

            # add the file and its associated mtime to the list of files
            and $files{$File::Find::name} = stat($File::Find::name)->mtime

              # static rendering bits
              and (
                param('-all') 
                or !-f "$static_dir/$1/index." . $static_flavours[0]
                or stat("$static_dir/$1/index." . $static_flavours[0])->mtime < stat($File::Find::name)->mtime
              )
                and $indexes{$1} = 1
                  and $d = join('/', (nice_date($files{$File::Find::name}))[5,2,3])

                    and $indexes{$d} = $d
                      and $static_entries and $indexes{ ($1 ? "$1/" : '') . "$2.$file_extension" } = 1

        } else {
          !-d $File::Find::name and -r $File::Find::name and $others{$File::Find::name} = stat($File::Find::name)->mtime
        }
      }, $datadir
    );

    return (\%files, \%indexes, \%others);
}



sub entries1 {
        package blosxom;
        my (%files, %indexes, %others);
        find(
            sub {
                # $depthより深ければ復帰
                my $curr_depth = $File::Find::dir =~ tr[/][];
                return if ($depth && $curr_depth > $depth);

                # 現在の時刻、ファイル名、更新時刻の設定
                my $time = time;
                my $name = $File::Find::name;
                my $mtime = stat($name)->mtime;

                # ディレクトリなら復帰
                return if (-d $name);
                # 読めないなら復帰
                return unless (-r $name);

                # ファイル名のマッチ
                my ($path, $fn) =
                    $name =~ m{^$datadir/(?:(.*)/)?(.+)\.$file_extension$};
                $path ||='';

                # パターンにマッチしていないか、indexか、
                # .で始まるファイル名なら、%otherに追加して復帰
                if (!$fn || $fn eq 'index' || $fn =~ /^\./) {
                    $others{$name} = $mtime;
                    return;
                }

                # 未来の記事だったら復帰 (許可していない場合)
                return if (!$show_future_entries && $mtime > $time);

                # %filesに追加
                $files{$name} = $mtime;

                # 静的表示の処理 (%indexesに追加)
                my $check_index = "$static_dir/$path/index.$static_flavours[0]";
                return unless (
                    param('-all')
                    || !-f $check_index
                    || stat($check_index)->mtime < $mtime
                );
                $indexes{$path} = 1;
                my $d = join('/', (nice_date($mtime))[5,2,3]);
                $indexes{$d} = $d;
                $static_entries and $indexes{($path ? "$path/" : '') . "$fn.$file_extension"} = 1;
            }, $datadir
        );
        return (\%files, \%indexes, \%others);
}

sub nice_date {
  my($unixtime) = @_;
  
  my $c_time = ctime($unixtime);
  my($dw,$mo,$da,$ti,$yr) = ( $c_time =~ /(\w{3}) +(\w{3}) +(\d{1,2}) +(\d{2}:\d{2}):\d{2} +(\d{4})$/ );
  $da = sprintf("%02d", $da);
  my $mo_num = $month2num{$mo};
  
  return ($dw,$mo,$mo_num,$da,$ti,$yr);
}

記事ファイルが101個ある状態で試してみたところ、思ったより速度は改善されているようです。

$ perl  test.pl
Benchmark: timing 200 iterations of test0, test1...
     test0: 15 wallclock secs (13.11 usr +  1.88 sys = 14.99 CPU) @ 13.34/s (n=200)
     test1: 10 wallclock secs ( 8.20 usr +  1.87 sys = 10.07 CPU) @ 19.86/s (n=200)
$ perl  test.pl -all=1
Benchmark: timing 200 iterations of test0, test1...
     test0: 11 wallclock secs (10.01 usr +  1.21 sys = 11.22 CPU) @ 17.83/s (n=200)
     test1:  9 wallclock secs ( 8.02 usr +  1.09 sys =  9.11 CPU) @ 21.95/s (n=200)