PHPのプログラムを高速に処理をさせる書き方を紹介します。
1回1回の処理に人が知覚出来る程の差はないですが、ループの中や実行回数が多い処理では塵も積もれば山になります。
同じ処理を書くのでも、初期の実装から処理時間を意識する事で後に続く負荷テストや改修作業をずいぶん楽にしてくれます。
知ってさえいれば手間がかかる内容ではないので、是非参考にしてみてください。
インクリメント、デクリメントは後ろではなく前に
一般的に書籍でよく見るのは後ろですが、前に付けた方が高速に動きます。
for文などループでよく使うので、じわじわと効いてきます。
// 遅い例 for ( $i = 0; $i < 10000; $i++ ) { ... } for ( $i = 0; $i < 10000; $i-- ) { ... } // 速い例 for ( $i = 0; $i < 10000; ++$i ) { ... } for ( $i = 0; $i < 10000; --$i ) { ... }
ループはfor()ではなくwhile()を使う
無限ループを起こしやすいなどの理由でwhile()が敬遠される事があるのですが、正しく使えばfor()より高速にループしてくれます。
//遅い例 for ( $i = 0; $i < 10000; ++$i ) { ... } // 速い例 $i = 0; while( $i < 10000 ) { … ++$i; }
ループの継続判定条件に関数を書かない
ループが回って判定される度に関数が実行される為です。
これはかなり大幅に時間が変わります。
関数程ではないですが、計算処理やキャストでも同様に遅くなります。
ループの継続判定条件はシンプルが一番です。
// 遅い例(1) for ( $i = 0; $i < count( $arr ); ++$i ) { ... } // 速い例(1) $cnt_tmp = count( $arr ); for ( $i = 0; $i < $cnt_arr; ++$i ) { ... } // 遅い例(3) $max = 2 * 100; for ( $i = 0; $i < $max; ++$i ) { ... } // 速い例(2) $max = 2; for ( $i = 0; $i < $max * 100; ++$i ) { ... } // 遅い例(3) $max = '100'; for ( $i = 0; $i < $max; ++$i ) { ... } // 速い例(2) $max = 100; for ( $i = 0; $i < $max; ++$i ) { ... }
配列の作成はループの外でやる
上記の派生系ですが、array()を実行すると都度配列を作る処理が走るため遅くなります。
ループの外で作った配列を参照する形にした方が良いです。
// 遅い例 for ( $i = 0; $i < 10000; ++$i ) { if ( in_array( 1, array( 1, 2, 3, 4, 5 ) ) ) {} } // 速い例 $hoge = array( 1, 2, 3, 4, 5 ); for ( $i = 0; $i < 10000; ++$i ) { if ( in_array( 1, $hoge ) ) {} }
配列内の存在チェックはisset()を使う
配列内の要素を丁寧に探すよりも、要素とキーを逆転させてissetで存在確認した方が速い場合があります。
ただし、要素の重複がある場合や、配列のキーに使えないような内容の場合は使えないなどの制限と、array_flip()自体がループ内だとあまり意味ないですが。
// 遅い例 $list= range( 1, 100 ); for ( $i = 0; $i < 10000; ++$i ) { if ( in_array( $i, $list ) ) { ... } } // 速い例 $list = range( 1, 100 ); $list = array_flip( $list ); for ( $i = 0; $i < 10000; ++$i ) { if ( isset( $list, $i ) ) { ... } }
文字数字を何度も計算する場合は先にキャストしておく
文字数字を計算すると暗黙的にキャストが走るので、数値を計算するよりも遅くなります。
ループの中で計算する場合など、何度も計算に使う場合は事前に数値にしておくと速くなります。
// 遅い例 $val = '10'; for ( $i = 0; $i < 10000; ++$i ) { $tmp += $val; } // 速い例 $val = 10; $val = (int)$val; for ( $i = 0; $i < 10000; ++$i ) { $tmp += $val; }
比較演算子「==」「!=」は、型を厳密に「===」「!==」で判定する
「==」「!=」では、型の変換が入る分だけ遅くなります。
// 遅い例 if ( $boolValue == true ) { ... } // 速い例 if ( $boolValue === true ) { ... }
bool値の条件判定は比較演算子を使わない
比較演算子を使った結果は true か false になるので、元々bool値であれば比較演算子は不要です。
比較演算子を使うと、比較判定が入る分だけ遅くなります。
// 遅い例 if ( $boolValue === true ) { ... } if ( $boolValue === false ) { ... } // 速い例 if ( $boolValue ) { ... } if ( !$boolValue ) { ... }
配列を繋いで文字列化する際は、implode()ではなくjoin()を使う
PHP関数マニュアルを見るとjoin()はimplode()のエイリアスらしいのですが、何故か試してみると何故かjoinの方が高速に動きます。
(バージョンによる等があるかもしれません)
// 遅い例 $str = implode ( ',', $arr ); // 速い例 $str = join ( ',', $arr );
文字列を分解する際は、explode()ではなくpreg_split()を使う
preg_splitの方が正規表現が使える分だけ遅い印象なんですけどね…。
文字コードにも注意ください。
(※分解する条件や内容によっては逆転する可能性があるので注意)
// 遅い例 explode( ',', $str ); // 速い例 preg_split( '/,/', $str );
文字列は「.」で繋ぐ
ダブルクォーテーションの中に変数を入れて文字列を連結する事が出来ますが、素直に演算子「.」で繋いだ方が速いです。
// 遅い例 $tmp1 = "test $tmp2" // 速い例 $tmp1 = 'test ' . $tmp2;
配列の要素有無はbool型にキャストした結果で判定する
配列をbool型にキャストすると、空の配列はfalseに、値のある配列はtrueになります。
中身が空かどうかは、要素数を数えるよりbool型にキャストして判定した方が高速に判定出来ます。
なお、最初の要素が入っているインデックス値が分かっている場合はissetを使うとさらに高速に判定出来ます。
// 遅い例 if ( count( $arr ) > 0 ) { ... } // 速い例 if ( (bool)$arr ) { ... } // もっと速い例 if ( isset( $arr[ 0 ] ) ) { ... }
画面表示はprint()ではなくechoを使う
printとechoはどちらも画面に表示する際に使えるのでどちらでも良さげですが、処理内容は微妙に異なる様子。
速度にも違いがあり、echoを使った方が早く動きます。
// 遅い例 print ''; // 速い例 echo '';
複数の文字列をechoする時は「,」で繋ぐ
echoは引数を複数渡す事でまとめて画面表示させられます。(print()では出来ません)
文字列を繋いでからechoするよりも、複数まとめて出力してしまった方が高速です。
// 遅い例 echo '' . ''; // 速い例 echo '' , '';
定数ではなくグローバル変数を使う
define()を使うよりも$GLOBALS[]でグローバル変数を参照した方が高速だったりします。
可読性が落ちるので使い方にはご注意を。
// 遅い例 define( 'TEST', 1 ); function test() { return TEST; } // 速い例 $GLOBALS[ 'TEST' ] = 1; function test() { return $GLOBALS[ 'TEST' ]; }
if … else … 構文では実行する頻度が高い方をelse側にする
if側よりもelse側の方が分岐が速く動きます。
ただし、無理やりelse側にするのに条件判断の処理時間を増やしたら意味がないので無理が無い範囲で。
なお、elseifがある場合は条件判定が増えてしまう為、if側に高頻度な処理を置くのが早くなります。
// 遅い例 if ( ... ) { // 高頻度な処理 ... } else { // 低頻度な処理 ... } // 速い例 if ( ... ) { // 低頻度な処理 ... } else { // 高頻度な処理 ... }
switch … case … 構文では、頻度が高いCaseを前に書く
前から順番に判断していくので、当然前の方が速くなります。
// 遅い例 switch( ... ) { // 低頻度な処理 ... case '...': break; case '...': // 高頻度な処理 ... break; } // 速い例 switch( ... ) { // 高頻度な処理 ... case '...': break; case '...': // 低頻度な処理 ... break; }
同じ値を複数の変数に格納する場合は行を分ける
まとめた方が早そうに見えますが、分けた方が高速に動きます。
// 遅い例 $tmp1 = $tmp2 = 1; // 速い例 $tmp1 = 1; $tmp2 = 1;
三項演算子はifに変える
三項演算子を使うよりも、シンプルにif…else…を使った方が高速に動きます。
// 遅い例 $hoge = true ? 1 : 2; // 速い例 if ( true ) { $hoge = 1; } else { $hoge = 2; }
文字列から1文字抜く際は配列のように抜く
文字列に2バイトコードが含まれていないことが前提ですが、配列のように位置を指定して抜いた方が高速です。
// 遅い例 $str = 'abcdefg'; $v = substr( $str, 5, 1 ); // 速い例 $str = 'abcdefg'; $v = $v[ 5 ];
既存の文字列変数に文字列を繋げる際は「.=」を使う
繋いでから代入するよりも「.=」を使った方が高速です。
// 遅い例 $hoge = $hoge . $i; // 速い例 $hoge .= $i;
文字列の置換はpreg_replace()よりstr_replace()の方が速い
ただし、str_replace()で事足りる場合に限りますが。
// 遅い例 preg_replace( '/MNOPQ/', 'QPONM', $str ); // 速い例 str_replace( 'MNOPQ', 'QPONM', $str );
変数がbool型か調べる際は比較演算子の方が速い
ただ、可読性が悪いのでコレはオススメしません。
// 遅い例 if ( is_bool( $v ) ) { ... } // 速い例 if ( $v === true || $v === false ) { ... }