jocode.pl, Jcode.pm, Encode.pmのパフォーマンス比較/一番速いのはどれ?

総閲覧回数:4,034,776回 / ブログ拍手:2,630
作品DB等各サービスの機能追加情報や、技術系・面白系記事を中心に提供。
記事の投稿は基本Twitterでも告知させて頂いています。
連絡は作品DBの論客の方なら私書、DB外ユーザの方ならメールTwitterで可能です。
アクセス記録[推移 / PV内訳(過去1日 / 過去1週間) / 外部アクセス元 (昨日 / 過去1週間) / ログイン論客足跡]
プロフィール私書(メール)
   /   /送済
評価(一覧   /)
投票   /共:   /
ファン登録
作品/情報/
DB構築()
ブログ
[書く]
攻略記事リンク集
My Play List
<=次の記事 PC版日記に短縮URLへの連携リンクを追加
=>前の記事 最速一括検索::ブログ一括検索の対象にmixi, gree, モバゲー,手書きブログを追加して日本中の日記を網羅

1.
2009/11/11 Perl > 文字コード > jocode.pl, Jcode.pm, Encode.pmのパフォーマンス比較/一番速いのはどれ?」
[この書込みのみ表示(記事URL紹介用) / 編集 / 削除 / トラバ送信 / 共有分類に追加(タグ付け)]拍手:1個

1. Perlの文字コード変換モジュールのパフォーマンス比較をしてみようと思ったきっかけ
2. 比較テスト結果
3. パフォーマンス比較テスト結果からの結論
4. 各モジュールのパフォーマンス比較プログラム

1. Perlの文字コード変換モジュールのパフォーマンス比較をしてみようと思ったきっかけ

ちょっと貧弱な環境でやってみたら、Jcodeの呼び出しの処理のところでOut of memoryでプログラムが落ちてしまうということがあった。
そこで、この際Encode.pmへの乗り換えを検討したのですが、結局Out of memoryになることには変わりなかった。
しかし、ちょっとでもパフォーマンス等が改善するのならば、Encode.pmに切替えておこうと思った。

とりあえず検索してみたところ
jcode.plが最速でした
というかんじのレポートが検索結果の上の方に見つかり拝見させて頂いたのですが、その結論と理由に納得感が得られなかったので(書いている方も他の人ももっと処理がかかると思っている処理が速い/しかしその理由がない)、自分でパフォーマンス比較するプログラムを作ってみて検証してみることにしました。

結論から言うと、こちらでのベンチマーク結果だと、そっちの結論の「jcode.pl最強伝説」とは違い「Encodeモジュールが最強」となっています。

何故かなと思ってそっちのサイトのベンチマークプログラムを見てみましたが、そちらのプログラムではjcode.plの呼び出し方が変換しているつもりが実は変換していない為、実際には正常処理しないまま次から次の処理へと計測されていただけのようでした。
処理が異常に速いと書かれているjcode.plの型グロブの処理のところを
jcode::convert(*text, 'sjis');
print $text;
とprintして$textの中身を確認してみるとShift_JISに変換されていないことが分かります。
これは、型グロブでの変換には
require 'jcode.pl';
my text = "....";
jcode::convert(*text, 'sjis');
という風にmyをグロブする変数に付けてしまうと名前空間がmainとしてつき変わり、別の名前空間(jcode)にある関数に型グロブとして上手く引き渡せない為です(myを外すと変換できる / でも綺麗じゃないからそもそもそういう風には使わない方が良い)。

もしかして本当にjcode.plが最速なのか...と期待したのでちょっと残念でもありますが、一般的に想定されるであろう妥当な結論にはなりました。
jcode系列で最新版のEncodeが最速というのはソフトの進化の仕方として正しいということで、この結果はEncode.pmを使っていく上で安心出来る結果なのではないかと思います。
2. 比較テスト結果

条件
ループ回数100回
eucの入力ファイルのサイズ1.19MB
utf8の入力ファイルのサイズ1.65MB
jcode.plのバージョン2.13
Jcodeのバージョン0.83
Encodeのバージョン2.13
Encode::EUCJPMSのバージョン0.07
Encode::JP::H2Zのバージョン2.02

EUC-JP→Shift_JISの文字コード変換(携帯版への対応でよく使う変換)
jcode.pl44秒
Jcode54秒
Encode41秒

全角文字を半角文字に変換(携帯版への対応でよく使う変換)
jcode.pl56秒
Jcode42秒
Encode::JP::H2Z5秒

UTF8→EUC-JPへの変換(外部サイトのAPI対応でよく使う変換)
Jcode8秒
Encode6秒

3. パフォーマンス比較テスト結果からの結論

EUC→Shift_JISに変換する処理でEncodeはJcodeの1.31倍速、jcode.plに比べると1.07倍速
UTF8→EUC-JPに変換する処理でEncodeはJcodeの1.33倍速
全角→半角に変換する処理でEncode::JP::H2ZはJcode.pmの8.4倍速、jcode.plに比べると11.2倍速

全パターンでEncode.pmが最善のパフォーマンスなので、迷わずEncode(Encode::EUCJPMS), Encode::JP::H2Zを選んで良いようです。
とりわけ全角文字を半角文字に変換する処理のパフォーマンス向上が著しいので、そこの処理に旧来のモジュールが文字コード変換なみの時間がかかっていることを考えると、携帯用に全角文字を半角にしたり、検索の為に文字コードを全角に統一したりする時に、大きなパフォーマンス向上が得られそうです。

なお、プログラムだけで処理出来るEUC-JP→Shift_JISより対応表が必要なUTF8→EUC-JPの方が7倍近く速いというのはちょっと予想外だったので、暇があったら後でどうしてそうなっちゃっているのか理由を調べてみようかなと思いました。
4. 各モジュールのパフォーマンス比較プログラム

ご自分の環境でも試してみて下さい。
使ったことがない方には文字コード操作の実例として参考になるかもしれません。

jcode.plのダウンロードはこちらから
他は
su -
でrootになってから
perl -MCPAN -e shell
と打って、そこで
install $MODULE_NAME
でインストール出来ます。

#!/usr/bin/perl -w
=pod
Script for comparing the performance of jcode.pl, Jcode and Encode
Created by Hajime Kurita
Distributed at http://www.accessup.org/pj/6_B4C9CDFDBFCDA4B5A4F3/4/list.html

Usage:
perl bench_moji.pl $UTF8_FILE;
=cut
use strict;
use Benchmark;
use Encode::EUCJPMS; # MS拡張に対応したEUCを使えるように
use Encode::JP::H2Z;
use FileHandle;
use Jcode;
require './jcode.pl'; # jcode.plはこのスクリプトと同じディレクトリにあると想定 / このディレクトリ内でスクリプトは実行すること

my $file='/www/search/public_html/dev/j_diary.xml';
foreach my $ARGV (@ARGV){
  $file=$ARGV[0]; # utf8のファイル
}
die "Please specify file encded in utf8" unless $file;

my $max=100;
my %orig=();
if(my $fh=new FileHandle($file)){
  local $/=undef;
  $orig{'utf8'}=<$fh>;
  $fh->close();
}
else{
  die $file." doesn't exist.";
}
my $jc=new Jcode('');
my %length=();
$orig{'euc'}=$jc->set($orig{'utf8'}, 'utf8')->euc();
$length{'utf8'}=length($orig{'utf8'});
$length{'euc'}=length($orig{'euc'});

my @mod=('jcode', 'Jcode', 'Encode');
my @from_to=('euc_sjis','utf8_euc');
my @todo=('conv', 'z2h');

print "Loop\t".$max."\n";
foreach my $from_to (@from_to){
  if($from_to eq 'utf8_euc'){
    print 'utf8 input length'."\t".$length{'utf8'}."\n";
  }
  else{
    print 'euc input length'."\t".$length{'euc'}."\n";
  }
}

foreach my $from_to (@from_to){
  foreach my $todo (@todo){
    foreach my $mod (@mod){
      next if($from_to eq 'utf8_euc' && $mod eq 'jcode');
      next if($from_to eq 'utf8_euc' && $todo eq 'z2h');
      my $start=new Benchmark;
      for(my $i=0;$i<$max;$i++){
        if($mod eq 'Encode'){
          if($from_to eq 'utf8_euc'){
            my $con=$orig{'utf8'};
            if($todo eq 'conv'){
              Encode::from_to($con, 'utf8', 'eucJP-ms');
            }
          }
          else{
            my $con=$orig{'euc'};
            if($todo eq 'conv'){
              Encode::JP::H2Z::z2h(\$con);
            }
            else{
              Encode::from_to($con, 'eucJP-ms', 'cp932');
            }
          }
        }
        elsif($mod eq 'jcode'){
          my $con=$orig{'euc'};
          if($todo eq 'conv'){
            &jcode::sjis($con, 'euc');
          }
          else{
     &jcode::z2h_euc(\$con)
          }
        }
        else{
          if($from_to eq 'utf8_euc'){
            my $con=$orig{'utf8'};
            if($todo eq 'conv'){
              $jc->set(\$con, 'utf8')->euc();
            }
          }
          else{
            my $con=$orig{'euc'};
            if($todo eq 'conv'){
              $jc->set(\$con, 'euc')->sjis();
            }
            else{
              $jc->set(\$con, 'euc')->z2h();
            }
          }
        }
      }
      my $end=new Benchmark;
      $from_to='euc_euc' if($todo eq 'z2h');
      my $ver='';
      my $show_mod=$mod;
      if($mod eq 'Encode'){
        if($todo eq 'z2h'){
          $show_mod='Encode::JP::H2Z';
          $ver=qq{$Encode::JP::H2Z::VERSION};
        }
        else{
          $ver=qq{$Encode::VERSION};
        }
      }
      elsif($mod eq 'jcode'){
        if($jcode::rcsid=~ m!\s(\d+\.\d+)\s!){
          $ver=$1;
        }
      }
      else{
        $ver=qq{$Jcode::VERSION};
      }
      print join("\t", $show_mod.' '.$ver, $todo, $from_to, timestr(timediff($end, $start)))."\n";
    }
  }
}

実行結果例
Loop    100
euc input length        1193879
utf8 input length       1650264
jcode 2.13      conv    euc_sjis        44 wallclock secs (43.64 usr +  0.04 sys = 43.68 CPU)
Jcode 0.83      conv    euc_sjis        55 wallclock secs (54.92 usr +  0.00 sys = 54.92 CPU)
Encode 2.37     conv    euc_sjis        42 wallclock secs (42.18 usr +  0.00 sys = 42.18 CPU)
jcode 2.13      z2h     euc_euc 55 wallclock secs (54.85 usr +  0.03 sys = 54.88 CPU)
Jcode 0.83      z2h     euc_euc 42 wallclock secs (42.30 usr +  0.00 sys = 42.30 CPU)
Encode::JP::H2Z 2.02    z2h     euc_euc  5 wallclock secs ( 4.58 usr +  0.00 sys =  4.58 CPU)
Jcode 0.83      conv    utf8_euc         8 wallclock secs ( 8.32 usr +  0.00 sys =  8.32 CPU)
Encode 2.37     conv    utf8_euc         6 wallclock secs ( 6.06 usr +  0.00 sys =  6.06 CPU)


コメントする1個


[他の記事も読む]
<=次の記事 PC版日記に短縮URLへの連携リンクを追加
=>前の記事 最速一括検索::ブログ一括検索の対象にmixi, gree, モバゲー,手書きブログを追加して日本中の日記を網羅


大分類が「Perl」の記事
この論客の記事全て
↑上へ