エンコーディング変換の高速化
当然の話だけど、対象の文字列が長くなると、Encode::encode も、$e->encode も大差ない。
ちょっといじって試してみる。
use strict; use warnings; use Benchmark qw/cmpthese timethese/; use utf8; use Encode; use Jcode; use Unicode::Japanese; use Smart::Comments; my $str = join '', ( 'a' .. 'z', ( map { chr } ord('ぁ') .. ord('ん') ) ); $str = $str x shift if @ARGV; my $bytes = encode_utf8($str); my $uj = Unicode::Japanese->new; my $j = Jcode->new; my $e = find_encoding('sjis'); cmpthese( timethese( 0, { 'U::J n' => sub { Unicode::Japanese->new( $str, 'utf8' )->sjis }, 'U::J s' => sub { $uj->set( $str, 'utf8' )->sjis }, 'Jcode n' => sub { Jcode->new( $str, 'utf8' )->sjis }, 'Jcode s' => sub { $j->set( $str, 'utf8' )->sjis }, 'E::ft' => sub { my $b2 = $bytes; Encode::from_to( $b2, 'UTF-8', 'Shift_JIS' ); }, 'E::e' => sub { encode( 'sjis', $str ) }, 'E oo' => sub { $e->encode($str) }, } ) );
文字列をそのまま使う場合と、100 倍長くした場合をDebian etch(Athlon 64 X2 4400+) で。
Rate E::ft U::J n Jcode n U::J s Jcode s E::e E oo E::ft 39473/s -- -35% -39% -58% -59% -59% -85% U::J n 60922/s 54% -- -6% -35% -36% -36% -77% Jcode n 64749/s 64% 6% -- -31% -32% -32% -75% U::J s 93982/s 138% 54% 45% -- -2% -2% -64% Jcode s 95475/s 142% 57% 47% 2% -- -0% -64% E::e 95475/s 142% 57% 47% 2% 0% -- -64% E oo 262993/s 566% 332% 306% 180% 175% 175% --
Rate E::ft U::J n U::J s Jcode n Jcode s E::e E oo E::ft 1037/s -- -62% -63% -65% -65% -65% -67% U::J n 2727/s 163% -- -2% -7% -8% -9% -12% U::J s 2773/s 168% 2% -- -5% -7% -7% -11% Jcode n 2925/s 182% 7% 5% -- -2% -2% -6% Jcode s 2973/s 187% 9% 7% 2% -- -1% -4% E::e 2997/s 189% 10% 8% 2% 1% -- -4% E oo 3110/s 200% 14% 12% 6% 5% 4% --
ついでに、Mac OS X(Core Duo 1.60 GHz) でも。
Rate E::ft Jcode n U::J n E::e Jcode s U::J s E oo E::ft 23144/s -- -42% -45% -55% -59% -66% -78% Jcode n 39659/s 71% -- -6% -23% -30% -42% -62% U::J n 42306/s 83% 7% -- -17% -25% -38% -59% E::e 51210/s 121% 29% 21% -- -9% -25% -51% Jcode s 56520/s 144% 43% 34% 10% -- -17% -45% U::J s 68495/s 196% 73% 62% 34% 21% -- -34% E oo 103603/s 348% 161% 145% 102% 83% 51% --
Rate E::ft Jcode n Jcode s E::e E oo U::J s U::J n E::ft 470/s -- -52% -53% -56% -57% -71% -72% Jcode n 984/s 110% -- -2% -7% -9% -39% -41% Jcode s 1002/s 113% 2% -- -5% -8% -38% -40% E::e 1057/s 125% 7% 5% -- -3% -35% -37% E oo 1085/s 131% 10% 8% 3% -- -33% -35% U::J s 1617/s 244% 64% 61% 53% 49% -- -3% U::J n 1675/s 257% 70% 67% 58% 54% 4% --
む、こちらは長くなった場合に Unicode::Japanese がやたらと早い。なんでだろ。
ちなみに、似たような話は Python にもある*1。例えば、str から unicode を作る方法はだいたい次の方法がある。
t = unicode(s, encoding)
t = s.decode(encoding)
import codecs decoder = codecs.getdecoder(encoding) t = decoder(s)[0]
文字列の長さを変えながら速度を測定。
#!/usr/bin/env python # -*- coding: utf-8 -*- import codecs from timeit import Timer iteration = 1000 encoding = 'shift_jis' for n in (2 ** n for n in xrange(10)): s = u' あ'.encode(encoding) * n timers = [] timers.append(Timer('unicode(%s, %s)' % (repr(s), repr(encoding)))) timers.append(Timer('%s.decode(%s)' % (repr(s), repr(encoding)))) timers.append(Timer('decoder(%s)[0]' % repr(s), 'import codecs; decoder = codecs.getdecoder(%s)' % repr(encoding))) print '%5d' % n, ' '.join('%.6f' % timer.timeit(iteration) for timer in timers)
1 0.139643 0.110270 0.057828 2 0.151211 0.117217 0.064562 4 0.152519 0.129006 0.069813 8 0.168138 0.140995 0.094859 16 0.191728 0.167559 0.118740 32 0.247785 0.225073 0.171301 64 0.330757 0.302506 0.255668 128 0.500264 0.476449 0.428612 256 0.843505 0.823368 0.776082 512 1.538498 1.505656 1.454657
これは、str から unicode を作る方法だけど、入出力を行う場合は
import codecs fp = codecs.open(filename, 'r', encoding) try: for line in fp: pass finally: fp.close()
とか、
import codecs fp = file(filename, 'r') try: fp = codes.getreader(encoding)(fp) for line in fp: pass finally: fp.close()
とかのほうがいいかも。