[PHP]ダブルクォートとシングルクォートの速度の差(ベンチマークテスト付

Read More

PHPには文字列を括るのに「"」(ダブルクォート)と「'」(シングルクォート)の2つがある。


ダブルクォートは変数を埋め込むことができるなどの特性があり、
シングルクォートはいくつかのエスケープシーケンス(※)を無効化することができる。
※エスケープシーケンスとは通常の文字列では表せない特殊な文字や機能を、規定された特別な文字の並びにより表したもの


print()やinclude()などにあてる文字列で使う事のあるこのクォーテーションマークだが、人によってダブルクォートとシングルクォートのどちらを使うか分かれる。


こだわりを持ってそれぞれを使い分けしている人もいれば、
常にダブルクォートを使う人もいる。
あるはその逆でシングルクォートのみを使う人もいる。


自分は文字列のみを扱う場合はシングルクォートを使うように心がけてきた。

これは、変数を埋め込んでいないただの文字列に対しては、
ダブルクォートよりシングルクォートの方が速い
と思っていたからだ。
# 昔何かの記事で読んだ気がする

つまり、ダブルクォートで括っていると、
PHPパーサの働きで「変数があるかも?」という処理にマシンパワーが消費されて、それらを積み重ねていくとわずかかもしれないが速度に差が出てくる、と思っていた。


だが今まで実際にPHPパーサでどの程度の違いが出ているのかきちんと調べたことがなかった。


ってことで色んなパターンのベンチマークテストを行ってみた。

実際テストを行ってみると思っていたのと違う結果が出たので少々驚いている。


なお、全てのベンチマークテストは以下のような条件で行った。
・一つのprint()に対してfor文を10万回ループさせてかかった総時間を計測。
・メモリの状態に左右されにくくするため1ループ毎にバッファクリアを行う。
・このテストを100回行い平均値を算出。
・テスト1回の結果は小数点第3位まで算出し、総テストの平均値のみ小数点第4位まで算出。
※記事の都合で平均値のみの掲載とする




まず試してみたのは以下のパターン。
print '私は 青空を 見るのが 好きです。';
print "私は 青空を 見るのが 好きです。";



結果
シングルクォートの文字列:
Average [ 0.0499 ] sec

ダブルクォートの文字列:
Average [ 0.0496 ] sec


ふむ。

ここでの結果を見てみると、
ダブルクォートもシングルクォートも差がない。

何度かやれば入れ替わったりするレベルだ。



次のテストは文字列の連結を行ったパターン。
print '私は'.' 青空を'.' 見るのが'.' 好きです。';
print "私は"." 青空を"." 見るのが"." 好きです。";



結果
シングルクォート同士の連結:
Average [ 0.0737 ] sec

ダブルクォート同士の連結:
Average [ 0.0730 ] sec



こちらも時間に大差はない。
わかったことはシングルクォートでもダブルクォートでも
文字列連結を行うだけで時間がかなりかかるようになるということ。



そこで今度は文字列連結の数を変えてみた。
結果を並べて掲載する。
連結数3:Average [ 0.0741 ] sec
連結数2:Average [ 0.0666 ] sec
連結数1:Average [ 0.0570 ] sec
連結数0:Average [ 0.0492 ] sec



連結数が1つ増えることによって時間にして約1.13倍増えている。
連結数0と連結数3の差は約1.5倍もある。

連結数は少ないほど良いということだ。



さて次はダブルクォートの特性ともいうべき、「変数を含める」もののパターン。
結果を比較しやすいように最後に 文字列と変数の連結のパターンもテストした。
$sJoint = '[joint]';
print "私は$sJoint 青空を$sJoint 見るのが$sJoint 好きです。";
print "私は{$sJoint} 青空を{$sJoint} 見るのが{$sJoint} 好きです。";
print "私は".$sJoint." 青空を".$sJoint." 見るのが".$sJoint." 好きです。";



結果
ダブルクォートの中に変数を埋め込み:
Average [ 0.1397 ] sec

ダブルクォートの中の変数"{}"(波カッコ)付き:
Average [ 0.1394 ] sec

ダブルクォートと変数の連結:
Average [ 0.1506 ] sec


ダブルクォートの中に変数を埋め込むパターンはどちらも大差ない。

こうしてみると変数と文字列の連結に時間がかかっている。


ちなみに変数に"{}"を使わないで埋め込みをするとマルチバイトのせいだろうか、変数が正しくパースされないことがあった。
なので今回は変数の直後に半角空白をいれている。

ダブルクォート内に変数を埋め込む場合は必ず"{}"を付けるよう癖付けた方がよさそうだ。


一応ただの文字列を表示するパターンも比較の為に載せておく。
print "私は[joint] 青空を[joint] 見るのが[joint] 好きです。";
Average [ 0.0946 ] sec


ただ文字列を出力するだけの結果だが、一番最初の結果と比べると少し遅くなっている。
まあこれは単純に扱うバイト数が増えたからだろう。



文字列と変数の連結に対して、文字列と文字列の連結はどのくらい時間に違いがあるのか気になったのでそれも調べてみた。
print "私は"."[joint]"." 青空を"."[joint]"." 見るのが"."[joint]"." 好きです。";
Average [ 0.1465 ] sec


文字列と変数の連結に比べると、文字列と文字列の連結の方が若干ではあるが速い。
だがこれは、変数と文字列の連結で時間がかかるというより単純に変数の解析に必要な時間だと思う。




はい総結論。
・シングルクォートもダブルクォートも文字列だけ扱うなら差はない。
・連結演算子の数は多ければ多いほど比例して処理が重たくなる。
・文字列と変数を同時に扱う場合は「連結」よりも「ダブルクォート内に変数を埋めた」方が速い。


今回の結果では"ただの文字列"を扱う場合、シングルクォートもダブルクォートもその差ははっきりと確認できなかった。
ただ、さらに長い文字列を使ってテストをしていればあるいは少しは差が出たかもしれない。

あくまでも俺の環境で、ということを忘れないで欲しい。




さてさて、今後のプログラミングに影響を与える結果となったか?



Comments(6)

1  いと  2012/04/19 (木) 11:59 ID:XXXXXXXXX
シングルクォートとダブルクォートで差がないなんて信じられないですね~
もしかしてループで回しているから、文字列部分のコンパイルを最初の1回しかしてないのかもですね。

2  115ちゃん  2012/04/19 (木) 12:33 ID:XXXXXXXXX
ふむふむ。
私も''の方が早いと信じて疑いませんでした。
今後は”{}”使っていこう~!!
azs

3  シラサヤ  2012/04/19 (木) 14:56 ID:XXXXXXXXX
>いと さん
なるほど、言われてみればキャッシュされているかも知れないなあ。
そうするとただのループ文の時間だけ算出したことになるかも…うぐ。

まだプログラム残っているのでちょっと$cntとか変化付けてもう一度やってみます。
$cntを連結しても全ての条件は同じなのでそれで差異が見れるかと。


>115 ちゃん
埋め込む場合は"{}"は付けた方がベターっすな。
俺は見た目が嫌いなので埋め込みをしてこなかった。

4  シラサヤ  2012/04/19 (木) 15:20 ID:XXXXXXXXX
取り急ぎ検証結果を。
※それぞれ100000ループ*100テスト

print '私は 青空を 見るのが 好きです。'.($cnt++);
Average [ 0.0906 ] sec
print "私は 青空を 見るのが 好きです。".($cnt++);
Average [ 0.0908 ] sec

うーん、やっぱり結果だけみたら差がない…

5  シラサヤ  2012/04/19 (木) 16:03 ID:XXXXXXXXX
師匠に怒られた。
軽い疑問からやり始めたんだけど、もっかいちゃんとやります。

6  115ちゃん  2012/04/27 (金) 12:21 ID:XXXXXXXXX
師匠に怒られた!ってシラサヤくんも怒られるんだ~。
私には想像できないな。。。www