2007年04月23日 01:30 [Edit]

perl tips - Encodeを速く使う方法

camel

はっきり言ってこれはフェアではない。

みかログ: ErlangとPerlの速度比較
Perl側は,Encodeが遅い. Encode::from_toがinplaceでコンバートしてしまうために,直前に文字列コピーがあるのも影響しているのかも

なぜなら、Encode::from_to()は速度ではなく、安全性に最適化しているから。


そもそもはじめからUTF-8、それもutf8フラグがたっている文字列にfrom_toを使うのはばかげている。

for(my $i = 0; $i < 0xffff; $i++) {
        my $str2 = $str;
        Encode::from_to($str2, "UTF-8", "Shift_JIS");
}

は単に

for my $i (0..0xffff) {
    Encode::encode("Shift_JIS", $str);
}

でよい。

しかし、この場合はこれも最適解ではない。Encodeでは文字コード名の名前解決もしているからだ。"sjis"も"Shift_JIS"も同じに扱うためには、当然なんらかの形でこれらが同じであることを判定しなければならない。それを担うのが、Encode::find_encoding()だ。

実はこのEncode::find_encoding()、返すのは正規化された名前ではなくオブジェクトである。そして、実のところこのオブジェクトこそが実際の(en|de)codeを行うtranscoderなのである。よって最適解は、以下のとおりとなる。

my $enc = find_encoding("Shift_JIS");
for my $i (0..0xffff) {
    $enc->encode($str);
}

以上を吟味した上で実行してみたのが、以下のbenchmarkである。

            Rate   E::ft  U::J n Jcode n  U::J s Jcode s    E::e    E OO
E::ft    27655/s      --    -35%    -40%    -57%    -58%    -58%    -81%
U::J n   42639/s     54%      --     -7%    -34%    -35%    -35%    -71%
Jcode n  45809/s     66%      7%      --    -29%    -30%    -30%    -69%
U::J s   64725/s    134%     52%     41%      --     -1%     -1%    -56%
Jcode s  65330/s    136%     53%     43%      1%      --     -0%    -56%
E::e     65333/s    136%     53%     43%      1%      0%      --    -56%
E OO    148025/s    435%    247%    223%    129%    127%    127%      --

# Where
# E::ft    Encode::from_to
# U::J n   Unicode::Japanese->new for each iteration
# Jcode n  Jcode->new for each iteration
# U::J s   Unicode::Japanese with object reuse
# Jcode s  Jcode with object reuse
# E::e     Encode::encode
# E OO     Encode with object reuse

Benchmarkのソースを最後に添付しておいたので詳しくはそちらをご覧頂くとして、この通りEncodeは適切に使えばかなり高速なのである。

とはいうものの、find_encoding()を使ったテクニックは意外とまだ知られていないので、これを機会に紹介してみた。Encode 2.20でもこのテクニックをPODで紹介している。

Happy Encoding!

Dan the Encode Maintainer

use strict;
use warnings;
use Benchmark qw/cmpthese timethese/;
use utf8;
use Encode;
use Jcode;
use Unicode::Japanese;

my $str = join '', ( 'a' .. 'z', ( map { chr } ord('ぁ') .. ord('ん') ) );
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) },
        }
    )
);

この記事へのトラックバックURL

この記事へのトラックバック
404 Blog Not Found:perl tips - Encodeを速く使う方法
404 Blog Not Found:perl tips - Encodeを速く使う方法【】at 2012年01月19日 22:21
スクリプトのutf8化計画に役立つであろうコードを書いた。 スクリプトのあるディ...
久しぶりにPerlのコードを書いた【日曜プログラマのそゞろ事】at 2008年11月17日 00:27
弾さんがEncode::find_encoding()の使い方を解説していました。
[O] Encode::find_encoding()の使い方【Overlasting::Net】at 2007年04月27日 22:19
ref:404 Blog Not Found:perl tips - Encodeを速く使う方法 当然の話だけど、対象の文字列が長くなると、Encode::encode も、$e->encode も大差ない。 ちょっといじって試してみる。 use strict; use warnings; use Benchmark qw/cmpthese timethese/; use utf8; use En
[Perl][Python]エンコーディング変換の高速化【odz buffer】at 2007年04月23日 23:59
ErlangとPerlの速度比較の文字コード変換部分に関して, Danさんからト
ErlangとPerlの速度比較 その2【みかログ】at 2007年04月23日 23:11
この記事へのコメント
そんなことはありません。404 だけでPerl陣営と括るのは危険だと思います。
Posted by 405 at 2008年07月31日 16:31
どうしてこう、「この書き方では速度は引き出せない」と書けない
んでしょうか…

Perl陣営の人って、意図的でなくてもPerlに不利益なコンテンツが
あると、「これはPerlの敵である」と吊るし上げるのがデフォルト
なのでしょうか。
Posted by rero at 2007年04月25日 01:31