配列代入のコスト

調べものをしていたら配列の作成は結構コストを食うようだというのに気付きました。
ベンチとってみたので晒してみます。

サンプルデータは例によってキン肉マン

#!/usr/local/bin/perl

use strict;

use Benchmark qw(:all);

my @list = <DATA>;
# 運命の5,000王子
my @large_list = (@list) x 1000;

sub parse1 {
    return split "\t", $_[0];
}

sub parse2 {
    my $line = shift;
    return split "\t", $line;
}

sub parse3 {
    my $line = shift;
    my @line = split "\t", $line;
    return @line;
}

my $res = Benchmark::timethese(100, {
    "1.直接返す"           => sub { parse1($_) for @large_list },
    "2.引数を受ける"       => sub { parse2($_) for @large_list },
    "3.引数/Arrayを受ける" => sub { parse3($_) for @large_list },
});

Benchmark::cmpthese($res);

__DATA__
マリポーサ  メキシコ    100000000   偽マッスルリベンジャー
アタル  キン肉星    1080000 ナパームストレッチ
ビッグボディ    カナダ  100000000   メイプルリーフクラッチ
スーパーフェニックス    オーストラリア  100000000   マッスルリベンジャー
ゼブラ  ナムビア    100000000   マッスルインフェルノ
Benchmark: timing 1000 iterations of 1.直接返す, 2.引数を受ける, 3.引数/Arrayを受ける...
1.直接返す: 24 wallclock secs (23.91 usr +  0.00 sys = 23.91 CPU) @ 41.82/s (n=1000)
2.引数を受ける: 25 wallclock secs (25.05 usr +  0.00 sys = 25.05 CPU) @ 39.92/s (n=1000)
3.引数/Arrayを受ける: 38 wallclock secs (37.15 usr +  0.00 sys = 37.15 CPU) @ 26.92/s (n=1000)
                       Rate 3.引数/Arrayを受ける  2.引数を受ける      1.直接返す
3.引数/Arrayを受ける 26.9/s                   --            -33%            -36%
2.引数を受ける       39.9/s                  48%              --             -5%
1.直接返す           41.8/s                  55%              5%              --

リストに1回受ける(配列を作成する)だけで1.5倍強速度に差が出ました。
(引数受ける受けないはあんまり変わらないですね。)


で、更に

splitも中で配列を作ってるんじゃなかろうかと思いまして正規表現でパースしたのと比較してみました。

#!/usr/local/bin/perl

use strict;

use Benchmark qw(:all);

my @list = <DATA>;
# 運命の5,000王子
my @large_list = (@list) x 1000;

sub parse_split {
    return split "\t", $_[0];
}

sub parse_regexp {
    $_[0] =~ /^(.+)\t(.+)\t(.+)\t(.+)/;
    return ($1, $2, $3, $4);
}

my $res = Benchmark::timethese(1000, {
    "split"  => sub { parse_split($_)  for @large_list },
    "regexp" => sub { parse_regexp($_) for @large_list },
});

Benchmark::cmpthese($res);

__DATA__
マリポーサ  メキシコ    100000000   偽マッスルリベンジャー
アタル  キン肉星    1080000 ナパームストレッチ
ビッグボディ    カナダ  100000000   メイプルリーフクラッチ
スーパーフェニックス    オーストラリア  100000000   マッスルリベンジャー
ゼブラ  ナムビア    100000000   マッスルインフェルノ
Benchmark: timing 1000 iterations of regexp, split...
    regexp: 19 wallclock secs (19.81 usr +  0.00 sys = 19.81 CPU) @ 50.48/s (n=1000)
     split: 23 wallclock secs (22.53 usr +  0.00 sys = 22.53 CPU) @ 44.39/s (n=1000)
         Rate  split regexp
split  44.4/s     --   -12%
regexp 50.5/s    14%     --

微妙な差ですな。


しかしながら
ウン百万件あるログのパース処理とかには少し気を使えば書き方一つで結構なチューニングが出来そうです。