Unicode で lc() uc() が遅い

use encoding したPerlスクリプトを組んでいて、lc()を使うと妙に実行速度が遅くなるのに気が付いたので、ベンチマークとってみました。ちなみに、perl 5.8.6 です。

use strict;
use encoding 'utf8';
use Benchmark;

my $word = join( '', ( 'ABCDEFGHIJKLMN', 'A'..'Z', 'a'..'z', 0..9 ) );

timethese( 1000000, {
  'lc' => sub {
    my $w = $word;
    lc( $w );
  },
  
  'tr' => sub {
    my $w = $word;
    $w =~ tr/A-ZA-Z/a-za-z/;
  },
} );

このような結果に。tr の方が断然速いですね。もっとも、lc() は日本語全角アルファベットの大文字・小文字変換もしてくれますが、他にどこまでの文字を変換してくれているのかは把握しきれてないのですが・・・。

Benchmark: timing 1000000 iterations of lc, tr...
        lc: 23 wallclock secs (23.28 usr +  0.09 sys = 23.37 CPU) @ 42789.90/s (n=1000000)
        tr:  7 wallclock secs ( 7.13 usr +  0.02 sys =  7.15 CPU) @ 139860.14/s (n=1000000)

ちなみに、「use encoding」の1行をコメントアウトしてもう一度やってみると(もちろん全角の変換はしなくなります)、

Benchmark: timing 1000000 iterations of lc, tr...
        lc:  0 wallclock secs ( 0.79 usr +  0.00 sys =  0.79 CPU) @ 1265822.78/s (n=1000000)
        tr:  1 wallclock secs ( 1.09 usr +  0.01 sys =  1.10 CPU) @ 909090.91/s (n=1000000)

なるほど。Unicode の扱いはコストがかかるって聞いたことがありますが、こんなにも違うものなのですね。

正規表現についても、use bytes; するとだいぶ速くなります。バイト列の比較で差し支えない場合は、use bytes; するのも手ですね。

use strict;
use encoding 'utf8';
use Benchmark;

my @words = ( qw( A B C D E F G ), 'A'..'Z', 'a'..'z', 0..9 );

timethese( 200, {
  'unicode' => sub {
    foreach my $i ( 0..$#words ) {
      foreach my $j ( 0..$#words ) {
        $words[$i] =~ /$words[$j]/;
      }
    }
  },
  
  'bytes' => sub {
    use bytes;
    foreach my $i ( 0..$#words ) {
      foreach my $j ( 0..$#words ) {
        $words[$i] =~ /$words[$j]/;
      }
    }
  },
} );
 Benchmark: timing 200 iterations of bytes, unicode...
     bytes:  9 wallclock secs ( 9.05 usr +  0.04 sys =  9.09 CPU) @ 22.00/s (n=200)
   unicode: 17 wallclock secs (17.00 usr +  0.07 sys = 17.07 CPU) @ 11.72/s (n=200)