データ構造
本節では、
ある値が一覧に含まれるかはexists()を使う
ある文字列が一覧で定義した文字列と一致するかどうかを確認することは多いでしょう。たとえば、
次のコードでは、Reply-Toヘッダが一覧に含まれることの確認方法を、grep()関数と、exists()関数で比べたものです。
my $A = ['From', 'Reply-To', 'To'];
my $H = {'From' => 1, 'Reply-To' => 1, 'To' => 1};
sub usegrep {
    my $v = 'Reply-To';
    return 1 if grep { $v eq $_ } @$A;
}
sub hashkey {
    my $v = 'Reply-To';
    return 1 if exists $H->{ $v };
}考察:exists()がgrep()に快勝
表6に示すとおり、
| 比較項目 | 秒間実行回数 | 速度比 | 
|---|---|---|
| grep() | 2,166,065 | 1. | 
| exists() | 4,137,931 | 1. | 
では、grep-list-vs-exists-hash-100.に、exists()関数でハッシュのキーを調べる方法が20倍ほど高速でした。
このように大きな差が生まれる理由は、grep()関数exists()関数でハッシュのキーを確認する方法は計算量がO(1)です。つまり、
さて、Devel::Sizeモジュールを使うと、total_で計測したメモリサイズは、$Aが214バイト、$Hが380バイトでした。表7に示すとおり、
| 要素数 | 配列 | ハッシュ | サイズ比 | 
|---|---|---|---|
| 100 | 7. | 10. | 1. | 
| 10,000 | 781. | 1. | 1. | 
| 1,000,000 | 76. | 102. | 1. | 
要素数の少ないデータには配列を使う
前項のベンチマークでは配列よりもハッシュのキーが圧倒的に速かったため、
たとえばPerlのコアモジュールであるTime::Pieceは内部で日付データを配列として保持
次のコードは、Final-Recipient:の3要素を配列とハッシュで定義し、Final-Recipient:neko@nyaan.となるコードを、
my $A = ['final-recipient', 'rfc822', 'neko@nyaan.jp'];
my $H = {
    'field' => 'final-recipient',
    'type' => 'rfc822',
    'value' => 'neko@nyaan.jp'
};
sub ar { return $A->[0].': '.$A->[2] }
sub hs { return $H->{'field'}.': '.$H->{'value'} }考察:配列がハッシュに快勝
表8に示すとおり、
| 比較項目 | 秒間実行回数 | 速度比 | 
|---|---|---|
| ハッシュ | 4,761,905 | 1. | 
| 配列 | 6,185,567 | 1. | 
エラーメッセージの照合には配列を使う
バウンスメールに現れるエラーメッセージは多種多様です。メールサーバソフトウェアごと、
次のコードは、x修飾子grep()関数とindex()関数で、
my $Fa = 'Delivery failed, blacklisted by rbl';
my $Re = qr{(?>
    access[ ]denied[.][ ]ip[ ]name[ ]lookup[ ]failed
    |... # エラーメッセージパターンの正規表現がいくつか
    |blacklisted[ ]by
    )
}x;
my $Ar = [
    'access denied. ip name lookup failed',
    ..., # エラーメッセージ文字列がいくつか
    'blacklisted by',
];
sub regex { return 1 if $Fa =~ $Re } # 正規表現で照合
sub grep1 {
    # 配列に対するgrep()で照合
    return 1 if grep { index($Fa, $_) > -1 } @$Ar;
}考察:配列に対するgrep()が正規表現に圧勝
表9に示すとおり、grep()関数で回してindex()関数で照合するほうが、
| 比較項目 | 秒間実行回数 | 速度比 | 
|---|---|---|
| 正規表現 | 545,455 | 1. | 
| 配列に対するgrep( ) | 1,463,415 | 2. | 
正規表現と配列、Devel::Sizeモジュールのtotal_で計測したメモリサイズは、$Reは216バイト、$Arは457バイトでした。メモリ使用量は2倍以上の差ですが、
配列の末尾を参照するには負の添え字を使う
本項は、$#配列名よりも、
次のコードは、$#配列名を使った例と負の添え字を使った例で、1から100までの数字を入れた配列に対して、100)99)
my @A = (1..100);
sub ds { return ($A[$#A] + $A[$#A - 1]) }
sub ni { return ($A[-1] + $A[-2]) }考察:負の添え字が$#に楽勝
表10のように、$v[-1]と書きましょう。
| 比較項目 | 秒間実行回数 | 速度比 | 
|---|---|---|
| $v[$#] | 4,838,710 | 1. | 
| $v[-1] | 10,909,091 | 2. | 
ループ処理にはforeachを使う
本項も前項と同じく、forとforeachとwhileの3つの選択肢を比べましょう。
次のコードでは、1から256までの数字を要素に持つ配列から、forforeachとwhileを比較します。
sub loop1f {
    my $v = 0;
    my @p = (1..256);
    for( my $e = 0; $e < 256; $e++ ) {
        $v++ if $p[$e] % 2 == 0;
    }
    return $v;
}
sub loop2e {
    my $v = 0;
    my @p = (1..256);
    foreach my $e ( @p ) {
        $v++ if $e % 2 == 0;
    }
    return $v;
}
sub loop3w {
    my $v = 0;
    my @p = (1..256);
    while( my $e = shift @p ) {
        $v++ if $e % 2 == 0;
    }
    return $v;
}考察:foreachがwhileとC形式のforに快勝
表11に示すとおり、foreachの一強となりました。whileはイテレータが返す値を取り回すのにも使用しますが、forはめったに必要となりませんので、foreachの一択でしょう。
| 比較項目 | 秒間実行回数 | 速度比 | 
|---|---|---|
| while | 23,202 | 1. | 
| for | 29,326 | 1. | 
| foreach | 37,594 | 1. | 
しかし、whileをforeachに書き換えられるとは限りません。whileではshift()関数で代入時に偽となっていたコードが、foreachでは偽とならないケースがあります。詳しくは、fails-onforeach-loop.で確認してください。このような落とし穴もありますので、
全体の性能を測るプロファイリング
ここまで紹介した例のとおり、Benchmarkモジュールでのベンチマークは部分としてのコードを競わせるものです。部分的に高速であるからと言って、
筆者は、Devel::NYTProfモジュールを使ってプロファイリングをしたうえで、
nytprofhtmlで結果を見やすくする
Devel::NYTProfモジュールが出力するnytprof.はバイナリファイルですので、nytprofhtmlコマンドを実行すると作られるnytprof/をブラウザで開き、
プロファイリング用Makefile
とはいえ、profileターゲットを定義して、makeprofileを実行するだけで完了するようにしています。
CODE := 'Sisimai->make(shift, delivered => 1)'
MAIL := tmp/emails-for-speed-test
profile:
    perl -d:NYTProf -Ilib -MSisimai -lE $(CODE) $(MAIL)
    nytprofhtmlプロファイリング結果の検証
図1はwhileを、forforwhile
 
 
部分でも全体でもforがwhileに勝るので、
まとめ
インフラを触るときの戒め
実際、masterブランチで、
ただし、
なお、
さて、
本誌最新号をチェック!
WEB+DB PRESS Vol.130
2022年8月24日発売
B5判/168ページ
定価1,628円
(本体1,480円+税10%)
ISBN978-4-297-13000-8
- 特集1
 イミュータブルデータモデルで始める
 実践データモデリング
 業務の複雑さをシンプルに表現!
- 特集2
 いまはじめるFlutter
 iOS/Android両対応アプリを開発してみよう
- 特集3
 作って学ぶWeb3
 ブロックチェーン、スマートコントラクト、NFT



