色々調べると先人方が調べてくれています。
とはいえ、情報が散らばってるので個人的にまとめたかったのと
現状どのくらい効果あるのか知りたいので遊んでみる。
どのくらい効果あるのか検証は100回〜1000回計測での平均
PHPは5.6.30
VMで動かしてるからちょっと遅い^^;;;
インクリメントとデクリメント
\$i++とか\$i--とかを単体で下のように書くことはよくありますが
PHP
for($i = 0; $i < 100; $i++){ ... }
これ遅いらしい…
++とか--は後ろに書くより前に書けと。
PHP
//パターン1
for($i = 0; $i < 100000; $i++){}
//パターン2
for($i = 0; $i < 100000; ++$i){}
|
平均実行時間(s) |
パターン1 |
0.20112862586975 |
パターン2 |
0.16215991973877 |
約1.24倍の高速化
なかなか手の癖で++を先に書くのって慣れないけど^^;;;
型変換は関数よりキャスト
PHP
//パターン1
$val = '1';
for($i = 0; $i < 100000; ++$i){
$tmp = intval($val);
}
//パターン2
$val = '1';
for($i = 0; $i < 100000; ++$i){
$tmp = (int)$val;
}
|
平均実行時間(s) |
パターン1 |
0.10631687641144 |
パターン2 |
0.05006349086762 |
約2.1倍の高速化 |
|
関数とキャストを比べたら当然か^^;;;
intval()は入力値の基数を指定できるので、そういう必要があれば使いましょう。
数字(not数値)を計算で使うなら事前にキャストする
PHP
//パターン1
$val = '1';
$tmp = 0;
for($i = 0; $i < 100000; ++$i){
$tmp += $val;
}
//パターン2
$val = '1';
$val = (int)$val;
$tmp = 0;
for($i = 0; $i < 100000; ++$i){
$tmp += $val;
}
|
平均実行時間(s) |
パターン1 |
0.46492505073547 |
パターン2 |
0.27117309570312 |
約1.7倍の高速化 |
|
パターン1は毎回、数字(string)から数値(int)へキャストされるので遅い。 |
|
さすがに、このサンプルのような書き方を実際にはしないと思うが、 |
|
設定ファイルやどっかからもってきた値で数値になってるとは限らない場合は、 |
|
キャストしておくほうが確実に速くなりますね。 |
|
定数よりグローバル変数を使う ではなさそうだ…
PHP
//パターン1
define( 'MIZUKI', 7 );
$tmp = '';
for($i = 0; $i < 100000; ++$i){
$tmp = MIZUKI;
}
//パターン2
$GLOBALS[ 'MIZUKI' ] = 7;
$tmp = '';
for($i = 0; $i < 100000; ++$i){
$tmp = $GLOBALS[ 'MIZUKI' ];
}
|
平均実行時間(s) |
パターン1 |
0.027095818519592 |
パターン2 |
0.048010039329529 |
定数の方が速かった |
|
定数として使うなら定数として定義すべきだと思うので、グローバル変数が速くても推奨できないなぁと思っていたが(´∀`)ホッ-3 |
|
ループはforよりwhile ではなさそうだ…
無限ループの危険があるwhileですが、forより速いという文献がちらほら。
PHP
//パターン1
for( $i = 0; $i < 100000; ++$i){}
//パターン2
$i = 0;
while($i < 100000){
++$i;
}
|
平均実行時間(s) |
パターン1 |
0.17190592288971 |
パターン2 |
0.14495255947113 |
約1.19倍の高速化
見通しの良いコードが書けていれば不安はないけど、
インクリメント忘れてた怖いなと^^;;;
ループの継続判定条件に関数等を書かない
これは、効果検証するまでもなくループ毎の判定で関数が動くんだから遅いのは明白…
PHP
for($i = 0; $i < count( $array ); ++$i) { ... }
なんて書いてたらぶっ飛ばす^^;;;
ループ外に出せる処理は外でやる
PHP
//パターン1
for($i = 0; $i < 100000; ++$i){
$key = array_search(1, array(1,2,3,4,5,6,7) );
}
//パターン2
$array = array(1,2,3,4,5,6,7);
for($i = 0; $i < 100000; ++$i){
$key = array_search(1, $array );
}
|
平均実行時間(s) |
パターン1 |
0.7890996217728 |
パターン2 |
0.3615311384201 |
約2倍の高速化 |
|
パターン1は毎回配列を生成してるので、普通に考えれば自明の理ですね。 |
|
比較演算子「==」や「!=」は「===」や「!==」で判定する
PHP
//パターン1
$val = 1;
for($i = 0; $i < 100000; ++$i){
if($val == 1){ }
}
//パターン2
$val = 1;
for($i = 0; $i < 100000; ++$i){
if($val === 1){ }
}
|
平均実行時間(s) |
パターン1 |
0.26900029182434 |
パターン2 |
0.29546132087708 |
あれ?「===」の方が遅くなってる… |
|
「==」の方が型変換が行われるから遅いんだよ(`・ー・´)ドヤ! |
|
って書こうと思ったのに謎だ。 |
|
Bool値を条件判定に使うときは、そのまま使う
PHP
//パターン1
$bool = true;
for($i = 0; $i < 100000; ++$i){
if($bool === true){ }
}
//パターン2
$bool = true;
for($i = 0; $i < 100000; ++$i){
if($bool){ }
}
|
平均実行時間(s) |
パターン1 |
0.29934306144714 |
パターン2 |
0.24208245277405 |
約1.2倍の高速化 |
|
大きな速度差は出なかったものの、 |
|
パターン1だと$bool === true という$boolがtrueか否かを判定し |
|
その結果をif文の判定へと投げているので、 |
|
パターン2のいきなりif文にtrueですけど判定してとした方が速い |
|
という流れの違いは認識しておいた方がよいでしょう。 |
|
三項演算子よりif文
PHP
//パターン1
$hoge = null;
for($i = 0; $i < 100000; ++$i){
$hoge = true ? 1 : 0;
}
//パターン2
$hoge = null;
for($i = 0; $i < 100000; ++$i){
if( true ){
$hoge = 1;
}
else{
$hoge = 0;
}
}
|
平均実行時間(s) |
パターン1 |
0.0313186645508 |
パターン2 |
0.0309974193573 |
ほぼ同じ |
|
ではあるのですが、実は何回も検証してみました。 |
|
結果、ほぼ同じ事はあってもif文の方が安定して速い。 |
|
昔、三項演算子の方が速い的な何かを見た記憶があり、
たしかに文より演算子の方が速そうだしと納得していたのでビックリ。
三項演算子の方が複数行にならないのでシンプルに見えるので…
色々思うところはあるし、ケースバイケースだとも思うのですが
若干宗教戦争がありそうなので沈黙^^;;;
if文は頻度の高い分岐側をelseにする
PHP
//パターン1
for($i = 0; $i < 100000; ++$i){
if( true ){
}
else{
}
}
//パターン2
for($i = 0; $i < 100000; ++$i){
if( false ){
}
else{
}
}
|
平均実行時間(s) |
パターン1 |
0.023554921150208 |
パターン2 |
0.021920537948608 |
若干速い |
|
が!無理にやると分かりにくいコードになるし、 |
|
そこまでしても、劇的に速くなるわけではないので。 |
|
switch文は頻度の高い条件を先に書く
PHP
//パターン1
for($i = 0; $i < 100000; ++$i){
$val = 7;
switch( $val ){
case 1: break;
case 2: break;
case 3: break;
case 4: break;
case 5: break;
case 6: break;
case 7: break;
default:
}
}
//パターン2
for($i = 0; $i < 100000; ++$i){
$val = 1;
switch( $val ){
case 1: break;
case 2: break;
case 3: break;
case 4: break;
case 5: break;
case 6: break;
case 7: break;
default:
}
}
|
平均実行時間(s) |
パターン1 |
0.117320728302 |
パターン2 |
0.042806816101 |
約2.7倍の高速化 |
|
この検証はあくまで速度差がどの程度でるかという点なので |
|
実際はそれなりに各caseに分岐するだろうから |
|
速度という面では銀の弾丸でないです。 |
|
ただ、可読性的に悪くならないのであれば、頻度の高いcaseを |
|
上の方に書いた方がいいよねと心にしまっておいても損はないでしょう。 |
|
文字列は「.」で連結
PHP
//パターン1
$str = "水樹奈々";
for($i = 0; $i < 100000; ++$i){
$tmp = "スマイルギャング $str";
}
//パターン2
$str = "水樹奈々";
for($i = 0; $i < 100000; ++$i){
$tmp = "スマイルギャング " . $str;
}
|
平均実行時間(s) |
パターン1 |
0.64041557312012 |
パターン2 |
0.50412213802338 |
約1.27倍の高速化 |
|
””の中に変数展開するより連結した方が速そうなのは雰囲気的にもわかる。 |
|
文字列が入っている変数への連結は「.=」
PHP
//パターン1
for($i = 0; $i < 100000; ++$i){
$str = "水樹奈々 ";
$str = $str . "スマイルギャング";
}
//パターン2
for($i = 0; $i < 100000; ++$i){
$str = "水樹奈々 ";
$str .= "スマイルギャング";
}
|
平均実行時間(s) |
パターン1 |
0.057024383544922 |
パターン2 |
0.053062963485718 |
気持ち速い |
|
PHPの内部を追っていないので詳細は不明ですが、 |
|
連携して、その結果を代入という手順より速い中間コードになっているでしょう。 |
|
それほど差は無いので、どちらでもいいかなぁ。
文字列からの1文字抽出は配列として抽出する
PHP
//パターン1
$str = "mizukinana";
for($i = 0; $i < 100000; ++$i){
$val = substr( $str, 7, 1 );
}
//パターン2
$str = "mizukinana";
for($i = 0; $i < 100000; ++$i){
$val = $str[7];
}
|
平均実行時間(s) |
パターン1 |
0.15074155330658 |
パターン2 |
0.05103883743286 |
約2.9倍 |
|
赤い彗星レベルなのですが、まず2バイトコードは含んでちゃだめなので使い道があまりなさそう。 |
|
これがC言語とかなら挙動的にポインタと納得できるがPHPだしなんか気持ち悪い^^;;;
文字列の置換はstr_replace()の方がpreg_replace()より速い
PHP
//パターン1
$str = "MizukiHachi";
for($i = 0; $i < 100000; ++$i){
preg_replace('/Hachi/', 'Nana', $str);
}
//パターン2
$str = "MizukiHachi";
for($i = 0; $i < 100000; ++$i){
str_replace('Hachi', 'Nana', $str);
}
|
平均実行時間(s) |
パターン1 |
0.43134694099426 |
パターン2 |
0.20478415489197 |
約2.1倍 |
|
シンプルにstr_replaceで出来るなら、あえてpregを使う必要はないですね。 |
|
文字列を分解する時は、explode()よりpreg_split() ではなさそうだ…
PHP
//パターン1
$str = "水,樹,奈,々";
for($i = 0; $i < 100000; ++$i){
$array = explode(',',$str);
}
//パターン2
$str = "水,樹,奈,々";
for($i = 0; $i < 100000; ++$i){
$array = preg_split('/,/', $str);
}
preg_splitの方が圧倒的に遅かったんだが…
分解する文字列が短すぎたかもしれないので、必ずpreg_splitの方が遅い!
と言い切れないとは思うのだけど、正規表現で処理しているから遅いだろうなという印象どおり。
文字列が含まれているかはpreg_match()よりstrpos()
PHP
//パターン1
$val = "水樹奈々 ETERNAL BLAZE";
for($i = 0; $i < 100000; ++$i){
if( strpos( $val, '奈々' ) !== false ){}
}
//パターン2
$val = "水樹奈々 ETERNAL BLAZE";
for($i = 0; $i < 100000; ++$i){
if( preg_match( '/奈々/' , $val ) ){}
}
|
平均実行時間(s) |
パターン3 |
0.12690899372101 |
パターン4 |
0.29959218502045 |
約2.4倍の高速化 |
|
正規表現系はやっぱ遅い。 |
|
正規表現が不要な場面では使わないにこしたことはないわけで、 |
|
決して正規表現使ってるんだぜ(`・ー・´)ドヤ!とかしてはいけない。 |
|
正規表現より関数
タイトルがざっくりすぎ^^;;;
色々判定関数が用意されていて
ctype_alnum 英数字判定
ctype_alpha 英字判定
ctype_cntrl 制御文字判定
ctype_digit 数字判定
ctype_graph 空白以外の印字可能文字判定
ctype_lower 小文字判定
ctype_print 印字可能文字判定 *空白含む
ctype_punct 空白及び英数字以外文字判定 *ざっくりいうと記号判定
ctype_space 空白文字判定 *空白文字なので空白以外にもタブとか改行とかも含まれる
ctype_upper 大文字判定
ctype_xdigit 16進数判定
これらとかで出来るなら関数使いましょう的な話。
is_xxxxというので必要な判定が出来るケースなら、そういうのも視野に入れる。
PHP
//パターン1
$val = "77";
for($i = 0; $i < 100000; ++$i){
if( preg_match( '/^[0-9]+$/' , $val) ){}
}
//パターン2
$val = "77";
for($i = 0; $i < 100000; ++$i){
if( ctype_digit($val) ){}
}
|
平均実行時間(s) |
パターン1 |
0.30083012580872 |
パターン2 |
0.08202259540558 |
約3.7倍の高速化 |
|
ちょっぱや… |
|
同じ値の複数変数への代入は分けて行う
PHP
//パターン1
$tmp1 = 0;
$tmp2 = 0;
for($i = 0; $i < 100000; ++$i){
$tmp1 = $tmp2 = 1;
}
//パターン2
$tmp1 = 0;
$tmp2 = 0;
for($i = 0; $i < 100000; ++$i){
$tmp1 = 1;
$tmp2 = 1;
}
|
平均実行時間(s) |
パターン1 |
0.04150550365448 |
パターン2 |
0.02898190021515 |
約1.4倍の高速化 |
|
これは想像より速かったし、代入の代入するより可読性は良いと思うので |
|
こういう書き方した方がよいでしょう。 |
|
変数がbool型か調べる時は比較演算子を使う
PHP
//パターン1
$v = 1;
for($i = 0; $i < 100000; ++$i){
if( is_bool( $v ) ){}
}
//パターン2
$v = 1;
for($i = 0; $i < 100000; ++$i){
if( $v === true || $v === false ){}
}
|
平均実行時間(s) |
パターン1 |
0.071662521362305 |
パターン2 |
0.039335107803345 |
約1.8倍 |
|
かなり速いが、ぱっと見何やってるの?って感じだし、 |
|
そんなにbool型かを判定することもないので |
|
あえてやる必要はないでしょう。 |
|
配列が空かの判定はbool型にキャストした結果で判定 ではなさそうだ…
PHP
//パターン1
$array = [1,2,3,4,5,6,7];
for($i = 0; $i < 100000; ++$i){
if( count($array) > 0 ){}
}
//パターン2
$array = [1,2,3,4,5,6,7];
for($i = 0; $i < 100000; ++$i){
if( (bool)$array ){}
}
//パターン3
$array = [1,2,3,4,5,6,7];
for($i = 0; $i < 100000; ++$i){
if( isset( $array[0] ) ){}
}
|
平均実行時間(s) |
パターン1 |
0.09809997081757 |
パターン2 |
0.24926652908325 |
パターン3 |
0.03348140716553 |
bool型にキャストしたら激遅 |
|
ここまで真逆な結果が出ると何か別の要因がありそうですが、 |
|
何度やっても同じ傾向が見られるので遅いのだろう。 |
|
パターン3は速いのだけど、配列が空では無いときに必ず存在する事の
担保がとれているインデックスがロジック上ないといけない。
あと、まったく事情を知らない人がコードを見たときに、
指定したインデックスの中身の存在を確認しているロジックだと
誤認してしまう可能性もあるので、個人的にはあまりオススメしない。
せめてempty()を使う方が、関数の意味的にもマッチしている。
速度はisset()よりは、やや速い
配列に要素を追加する時は、$array[]で代入
PHP
//パターン1
$array = [];
for($i = 0; $i < 100000; ++$i){
array_push($array, 'mizuki');
}
//パターン2
$array = [];
for($i = 0; $i < 100000; ++$i){
$array[] = 'mizuki';
}
|
平均実行時間(s) |
パターン1 |
0.23704917430878 |
パターン2 |
0.19576594829559 |
約1.2倍 |
|
パターン2の方がちょっと速い。 |
|
公式リファレンスにも1個加えるときはパターン2が良いよと書いてあるのでその通りなのだが、 |
|
array_push()は複数個を追加する時に使うという役割がメインだと思われるので |
|
複数個の時は見やすくなるしarray_push()で良いのではなかろうか。 |
|
array_search()やin_array()使うならループして判定 ではなさそうだ…
PHP
//パターン1
$array = ['HiroseSuzu','MizukiNana','KamishiraishiMone','KimuraFumino'];
for($i = 0; $i < 100000; ++$i){
if( array_search('MizukiNana', $array) ){}
}
//パターン2
$array = ['HiroseSuzu','MizukiNana','KamishiraishiMone','KimuraFumino'];
for($i = 0; $i < 100000; ++$i){
if( in_array('MizukiNana', $array) ){}
}
//パターン3
$array = ['HiroseSuzu','MizukiNana','KamishiraishiMone','KimuraFumino'];
$tmpArray = array_flip($array);
for($i = 0; $i < 100000; ++$i){
if( array_key_exists('MizukiNana', $tmpArray) ){}
}
//パターン4
$array = ['HiroseSuzu','MizukiNana','KamishiraishiMone','KimuraFumino'];
for($i = 0; $i < 100000; ++$i){
foreach ($array as $value) {
if($value == 'MizukiNana'){
break;
}
}
}
|
平均実行時間(s) |
パターン1 |
0.17810578346252 |
パターン2 |
0.17967174053192 |
パターン3 |
0.12337293624878 |
パターン4 |
0.12417731285095 |
ループの方が約1.4倍の高速化だけど… |
|
なかなか計測値が収束しなかったが、概ねarray_search()とin_array()は同程度の早さで |
|
パターン3とパターン4も同程度の早さ。 |
|
パターン3はflipしているので使えるケースは限定されるから
汎用的に対応でき速度もあるループが良さそうである。
が、要素数とヒットさせたい要素の位置にもよるので
ヒットさせる文字列をKimuraFumino
で行ってみたら…
array_search()もin_array()も遅くなったが
ループも匹敵する以上に遅くなる結果に。
ヒットさせる値の位置がどれくらい分散するかで平均速度は変わるけど
要素が増えるとループはかなり遅くなりそうなので、
関数を使った方が実行速度をある程度読めるのでよさそう。
配列の中身を連結しての文字列化は、implode()よりjoin() ではなさそうだ…
PHP
//パターン1
$array = [1,2,3,4,5,6,7];
for($i = 0; $i < 100000; ++$i){
$str = implode(',', $array);
}
//パターン2
$array = [1,2,3,4,5,6,7];
for($i = 0; $i < 100000; ++$i){
$str = join(',', $array);
}
かならずしもどちらが速いわけではない
ローカルPC上なので、完全に同じ負荷状態になってないから
それを踏まえると、実行時間はほぼ同じと判断してよさそう。
そもそもjoinはimplodeのエイリアスなので差が出ない方が正しいはず。
画面出力はprint()ではなくechoを使う
PHP
//パターン1
for($i = 0; $i < 100000; ++$i){
print '';
}
//パターン2
for($i = 0; $i < 100000; ++$i){
echo '';
}
|
平均実行時間(s) |
パターン1 |
0.028943252563477 |
パターン2 |
0.025534152984619 |
約1.1倍の高速化 |
|
そんなに変わらないなぁという印象なのと、 |
|
個人的にはechoばっかり使ってたので今まで通りでいいやという^^;;; |
|
echoで複数文字列の画面出力の時は「.」ではなく「,」で繋ぐ
PHP
//パターン1
for($i = 0; $i < 100000; ++$i){
echo '' . '';
}
//パターン2
for($i = 0; $i < 100000; ++$i){
echo '' , '';
}
|
平均実行時間(s) |
パターン1 |
0.04546332359314 |
パターン2 |
0.03141794204712 |
約1.4倍の高速化 |
|
結構差が出る。
よく echo "水樹奈々:年齢" . $age . "歳";
みたいな感じで書くので
ちりつもで速くなりそう。
現在時刻を取得時は$_SERVER['REQUEST_TIME']を使う
PHP
//パターン1
for($i = 0; $i < 100000; ++$i){
$now = time();
}
//パターン2
for($i = 0; $i < 100000; ++$i){
$now = $_SERVER['REQUEST_TIME'];
}
|
平均実行時間(s) |
パターン1 |
0.052512764930725 |
パターン2 |
0.049386715888977 |
ほぼ同じ |
|
関数呼び出し方は若干遅いけど、そこまで差はないので |
|
time()でかまわない気がする。 |
|
ダブルクォーテーションよりシングルクォーテーション
PHP
//パターン1
for($i = 0; $i < 100000; ++$i){
$str = "MizukiNana";
}
//パターン2
for($i = 0; $i < 100000; ++$i){
$str = 'MizukiNana';
}
ほぼ同じ
これぐらいシンプルな場合は特に気にするレベルではないが、
そもそも、ダブルとシングルで根本的に出来ることの違いがあるので
その辺を踏まえて検証してみると。
PHP
//パターン3
$val = "Mizuki";
for($i = 0; $i < 100000; ++$i){
$str = "${val}Nana";
}
//パターン4
$val = 'Mizuki';
for($i = 0; $i < 100000; ++$i){
$str = $val . 'Nana';
}
|
平均実行時間(s) |
パターン3 |
0.06001923084259 |
パターン4 |
0.047677421569824 |
約1.26倍の高速化 |
|
ダブルクォーテーションは中身を解釈してゴニョゴニョしてくれる分遅くなる。 |
|
文字列と変数の連結を何個もする必要がある場合はパターン3の方が |
|
可読性は良くなる感じもするが、こういうケースは実装物によっては |
|
頻繁に出てくるのでパターン4で書いた方が全体高速化の寄与できると思われる。 |
|