Text::MeCabの処理時間

Text::MeCabでのわかち書き処理が遅い件について、記録を取っていなかったので再度処理時間を計り直しました。

用意した文章は次のものをUTF-8に変換したものです。

MeCab

$ time mecab -Owakati < ~/spam-utf8.txt > /dev/null

real    0m0.009s
user    0m0.004s
sys     0m0.004s
$ time mecab -Owakati < ~/binzume_jigoku.txt > /dev/null

real    0m0.045s
user    0m0.032s
sys     0m0.012s
$ time mecab -Owakati < ~/kokushikan.txt > /dev/null

real    0m1.357s
user    0m1.336s
sys     0m0.020s

mecab-perl

$ cat wakati-mecab.pl
#!/usr/bin/perl -w
use strict;
use MeCab;

$/ = undef;
my $str = <STDIN>;

my $mecab = MeCab::Tagger->new("-Owakati");
my $tokenized = $mecab->parse($str);
print $tokenized;
exit 0;

$ time ./wakati-mecab.pl < spam-utf8.txt > /dev/null

real    0m0.049s
user    0m0.036s
sys     0m0.016s
$ time ./wakati-mecab.pl < binzume_jigoku.txt > /dev/null

real    0m0.086s
user    0m0.068s
sys     0m0.016s
$ time ./wakati-mecab.pl < kokushikan.txt > /dev/null

real    0m1.535s
user    0m1.308s
sys     0m0.228s

Text::MeCab

$ cat wakati-text-mecab.pl
#!/usr/bin/perl -w
use strict;
use Text::MeCab;

$/ = undef;
my $str = <STDIN>;

my @buf;
my $mecab = Text::MeCab->new({output_format_type => 'wakati'});
for (my $node = $mecab->parse($str); $node; $node = $node->next) {
   push(@buf, $node->surface);
}
my $tokenized = join(' ', @buf) . "\n";
print $tokenized;
exit 0;

$ time ./wakati-text-mecab.pl < spam-utf8.txt > /dev/null

real    0m0.043s
user    0m0.028s
sys     0m0.016s
$ time ./wakati-text-mecab.pl < binzume_jigoku.txt > /dev/null

real    0m0.641s
user    0m0.616s
sys     0m0.024s

$ time ./wakati-text-mecab.pl < kokushikan.txt > /dev/null
15分以上経っても終わらなかったので中断。

Text:Kakasi

$ cat wakati-kakasi.pl
#!/usr/bin/perl -w
use strict;
use Encode;
use Text::Kakasi;

$/ =undef;
my $text = <STDIN>;
my $kakasi = Text::Kakasi->new(qw/-iutf8 -outf8 -w/);
my $tokenized = Encode::encode_utf8($kakasi->get($text));
print $tokenized;

exit 0;

$ time ./wakati-kakasi.pl < spam-utf8.txt > /dev/null

real    0m0.101s
user    0m0.080s
sys     0m0.016s
$ time ./wakati-kakasi.pl < binzume_jigoku.txt > /dev/null

real    0m0.180s
user    0m0.144s
sys     0m0.036s
$ time ./wakati-kakasi.pl < kokushikan.txt > /dev/null

real    0m1.658s
user    0m1.576s
sys     0m0.084s

結果

ファイルサイズ[バイト] MeCab[秒] mecab-perl[秒] Text::MeCab[秒] Text::Kakasi
1767 0.009 0.049 0.043 0.101
25496 0.045 0.086 0.641 0.180
1039324 1.357 1.535 - 1.658

Text::MeCabの場合は文章サイズが大きくなると致命的に処理が遅くなるようです。


ちなみにText:MeCabによる『黒死館殺人事件』の処理を中断する前のtopコマンドの出力は次のようになります。

Tasks:  81 total,   2 running,  79 sleeping,   0 stopped,   0 zombie
Cpu(s): 100.0% us,  0.0% sy,  0.0% ni,  0.0% id,  0.0% wa,  0.0% hi,  0.0% si,
Mem:   2067640k total,   657180k used,  1410460k free,    98620k buffers
Swap:        0k total,        0k used,        0k free,   243604k cached

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
 5965 taki      25   0  240m 218m  34m R 99.7 10.8  12:04.22 wakati-text-mec

たかだか1Mバイトのテキストファイルを処理しているだけなのに異様にメモリを消費しています。

forループ等のオーバーヘッドが原因ではないと思うので、念のため確認をしてみました。『瓶詰地獄』の処理を擬似的に模すために5バイトの文字を5637回(実際のループ回数)加えてみました。

$ cat wakati-text-mecab-dummy.pl
#!/usr/bin/perl -w
use strict;

my @buf;
my $text = "12345";
for (1..5637) {
   push(@buf, $text);
}
my $tokenized = join(' ', @buf) . "\n";
print $tokenized;
exit 0;

$ time ./wakati-text-mecab-dummy.pl > /dev/null

real    0m0.018s
user    0m0.012s
sys     0m0.004s

比較すると次のようになり、forループ等の影響はそれほど大きくはないことがわかります。

MeCab[秒] mecab-perl[秒] Text::MeCab[秒] ダミー[秒]
0.045 0.086 0.641 0.018

こんなところですが、これで何かわかりますでしょうか?>dmakiさん