どうも鶏です。WordPressをつかっていると出力されたHTMLタグをちょこっと改変したい時ってありますよね?
たとえば特定の文字列にリンクをつけたいとか。
「これって正規表現は必要なのかな?」って感じのことをつぶやいたら捕捉されたので、この記事が生まれました。
こういう何かの文字列を置換する時にまず気をつけないといけないことがあります。それは「正規表現を使う必要があるかどうか考えること」。
正規表現で置換するのはとても便利で、条件に当てはまったものだけを置換できます。でも欠点があります。実行速度が遅いんです。とても遅い。すごく遅い。
単純に置換するのと比べると、だいたい2倍とか4倍とかそれくらいには遅い。「坊やだからさ・・・」のセリフで有名なシャアさんが操るザクも、正規表現をつかえばただのザクに成り下がる。
1回2回の正規表現での置換なら1/100秒程度の差くらいしかないかもしれません。
しかし文字列のサイズや回数が増えると倍々に膨れ上がり、それを何度も繰り返すことで0.5秒だとか1秒だとか3秒だとか処理が遅くなっていきます。
冒頭にあげた記事では、特定の文字列を正規表現で置換しています。
$categories = preg_replace('/\<\/a\> \((.*)\)/', ' ($1)', $categories );
WordPressのwp_list_categories関数で返ってきたHTMLタグを正規表現で置換するわけです。
カテゴリーA (3,075)
カテゴリーB (369)
カテゴリーC
カテゴリーD (11)
この文字列がこう変わる。
カテゴリーA (3,075)
カテゴリーB (369)
カテゴリーC
カテゴリーD (11)
ちょっと入れ替わっただけなので、正規表現をつかわなくてもできそうですよね。マッチ棒パズルみたいなものです。
正規表現を使わないでやってみる
ここでこうする。
$str3 = str_replace(array(' (', ')
'), array(' ', ''), $str);
※標準の出力には、)の後ろに改行があるのでマッチさせるために改行を入れてます。
速度を確かめよう
果たして赤い彗星は遅くなるのだろうか?速度を確かめてみた。
今回は以下の3つのサンプルを比較する。
サンプル
サンプルA(正規表現で置換)
$categories = preg_replace('/\<\/a\> \((.*)\)/', ' ($1)', $categories );
サンプルB(文字列置換)
$categories = str_replace(array(' (', ')
'), array(' (', ')
'), $categories);
サンプルC(文字列置換2回)
配列を使わずstr_replaceを2回実行する。
$categories = str_replace(' (', ' (', $categories);
$categories = str_replace(')
', ')
', $categories );
検証方法
検証はカテゴリーが50個あるブログを仮定して行う。記事数はランダムでカテゴリーの1/3が記事を1,000件以上もつ大きなブログである。
実行してみた
実際に各コードを実行して、どれくらいの速度で実行されるか出力してみた。
実行結果まとめ
|
サンプルA |
サンプルB |
サンプルC |
平均速度(秒) |
0.052506104 |
0.022947021 |
0.021905762 |
最大速度(秒) |
0.116210938 |
0.036132813 |
0.052978516 |
最小速度(秒) |
0.047851563 |
0.021728516 |
0.020751953 |
カテゴリーが200個になるとより差が広がる。
|
サンプルA |
サンプルB |
サンプルC |
平均速度(秒) |
0.189856445 |
0.088239746 |
0.085999756 |
最大速度(秒) |
0.373046875 |
0.114990234 |
0.119873047 |
最小速度(秒) |
0.181884766 |
0.086669922 |
0.083984375 |
お分かりいただけただろうか。2倍程度の差が発生している。
分析
3つのサンプルでは、preg_replaceよりもstr_replace、そしてstr_replaceを配列をつかって1回実行するよりも、文字列を指定して2回実行した方が早いという結果になった。
カテゴリー数が50個を超えてもコンマ数秒しか差がでないが、カテゴリーの一覧が呼び出されるたびにこれだけの差が生じる。サイドバーとフッターの二箇所に表示させてるブログも多いはず。表示させる箇所の数によって倍々になっていくのでコンマ何秒の差でも侮ってはいけない。
ただし1つの条件下の速度ばかり追い求めても別の条件下では遅くなる可能性もある。
たとえばカテゴリーの一覧に記事数が表示されない場合は「置換する」という動作そのものがいらなくなる。この時は「置換しない」ことで表示速度を大きく改善できるはず。
サンプルBを改造して、置換対象の文字列が存在するか検証すれば、条件から外れた場合のムダな置換による遅延を抑えることができる(サンプルB’)。検証のための配列のループとstrpos関数は実行速度がとても早いため、サンプルBと大きく変わることはない。
サンプルB’
// 検索する文字列
$search = array(' (', ')
');
// 置換する文字列
$replace = array(' (', ')
');
foreach ($search as $k => $v) {
// 検索する文字列が含まれているか検証
if (strpos($categories, $search[$k]) === false) {
// 含まれていなければ配列から削除
unset($search[$k], $replace[$k]);
}
}
if($search) {
// 配列があれば置換を実行
$categories = str_replace($search, $replace , $categories);
}
置換対象のある時(カテゴリー200件)
|
サンプルB |
サンプルC |
サンプルB’ |
平均速度(秒) |
0.085987061 |
0.085662842 |
0.089227539 |
最大速度(秒) |
0.115966797 |
0.108886719 |
0.127929688 |
最小速度(秒) |
0.083740234 |
0.083740234 |
0.087890625 |
置換対象のない時(カテゴリー200件)
|
サンプルB |
サンプルC |
サンプルB’ |
平均速度(秒) |
0.035809326 |
0.035501221 |
0.02234082 |
最大速度(秒) |
0.059814453 |
0.055175781 |
0.040039063 |
最小速度(秒) |
0.034667969 |
0.033691406 |
0.020751953 |
所感
文字列が短いとそれほど大きな差はでませんが、いつか何KB、何MBのテキストを置換する時のためにこういうコードを書くクセをつけておきたいですね。
この記事の検証は適当なローカルのテスト環境(専有サーバー)で検証したため、実行速度の遅くなるレンタルサーバーではもっと大きな差がでてくる可能性も高い。
WordPressの場合、テンプレート内にある正規表現を見直せばブログの表示速度が秒単位で改善されるかもしれません。ぜひ確かめてみてください。
でも、安心してください。どれだけ表示が遅くなってHTMLやエラーコードすら吐いてないようにみえても、ちょっと時間を置いて見てみればちゃんと吐いてますよ。
付録
テストコード置いときます。間違ってたらごめんなさい(´・ω・`)
$str = '';
for($i = 0;$i < 50;++$i) {
$r = rand(1,3);
if($r%3 === 1) {
$n = ' (' . number_format(rand(1000,9999)) . ')';
} elseif($r%3 === 2) {
$n = '';
} else {
$n = ' (' . number_format(rand(1,999)) . ')';
}
$str .= <<未分類{$n}
EOF;
}
echo '';
echo '
preg_replace
';
echo '';
echo '';
echo '
str_replace(置換×2)
';
echo '';
echo '';
echo '
str_replace(array)
';
echo '';
echo '';
関連
何か一言あれば!!
質問とかツッコミとかお気軽にコメントしてください。がんばって返します。