- 2006-10-27 (金) 19:05
- PHP
ウノウラボ Unoh Labs: PHPのちょっとしたコツに興味深い内容が書かれています。
1. array_pushは遅い
いくつかのBlogで語られてることですが、array_pushは、次のような書き方のほうが早いそうです。//array_puth($array, \'arraydata\'); $array[] = \'arraydata\';
これはほんとあちこちで言われていますね。実際にどの程度違うかベンチマークを取ってみました。
< ?php
require_once 'Benchmark/Timer.php';
$max = 10000;
$timer = new Benchmark_Timer();
$timer->start();
$array = array();
for ($i = 0 ; $i < $max ; $i++) {
$array[] = $i;
}
$timer->setMarker('statement');
$array = array();
for ($i = 0 ; $i < $max ; $i++) {
array_push($array, $i);
}
$timer->setMarker('array_push');
$timer->stop();
$timer->display();
?>
予想通りarray_pushの方が遅く、PHP4で約1.3倍、PHP5で1.6倍遅い結果になりました。
■PHP 4.4.4 (cli) -------------------------------------------------------- statement 1161939536.23285200 0.012247085571289 43.97% -------------------------------------------------------- array_push 1161939536.24843900 0.015587091445923 55.97% -------------------------------------------------------- ■PHP 5.1.6 (cli) -------------------------------------------------------- statement 1161939530.12842400 0.0053808689117432 37.91% -------------------------------------------------------- array_push 1161939530.13722100 0.0087971687316895 61.97% --------------------------------------------------------
2. array_key_existsよりハッシュを使え
array_searchは、毎回全データを検索するので遅いです。
データに配列の順序が関係ないなら、連想配列 + issetを使うほうが高速です。
こちらもベンチマークを取ってみました。
< ?php
require_once 'Benchmark/Timer.php';
$max = 10000;
$array = array('abc' => 'abc', 'null' => null);
$timer = new Benchmark_Timer();
$timer->start();
for ($i = 0 ; $i < $max ; $i++) {
isset($array['abc']);
isset($array['null']);
isset($array['none']);
}
$timer->setMarker('isset');
for ($i = 0 ; $i < $max ; $i++) {
array_key_exists('abc', $array);
array_key_exists('null', $array);
array_key_exists('none', $array);
}
$timer->setMarker('array_key_exists');
$timer->stop();
$timer->display();
?>
やはりarray_key_existsの方が遅いです。こちらはarray_pushより速度差がの方が大きく、PHP4で約2倍、PHP5で約4倍(!)遅い結果になりました。
■PHP 4.4.4 (cli) --------------------------------------------------------------- isset 1161938594.14881000 0.014264822006226 33.43% --------------------------------------------------------------- array_key_exists 1161938594.17719700 0.028387069702148 66.53% --------------------------------------------------------------- ■PHP 5.1.6 (cli) --------------------------------------------------------------- isset 1161938597.88852400 0.0043120384216309 19.44% --------------------------------------------------------------- array_key_exists 1161938597.90638100 0.01785683631897 80.50% --------------------------------------------------------
ただarray_key_existsとissetは全く同じ挙動を示すわけではなく、配列にキーが定義されているかを厳密に調べるにはarray_key_existsを使わざるを得ないようです。(@see PHPマニュアル-isset)
4. if文は「===」を使う
これは趣味の問題かもしれませんが、odz buffer – PHPのイテレーションの話とかでodzさんが書かれている形が自然だと思います。特に
if (is_null($null) === false) {
はis_null()がtrue or false以外の値を返すようで引っかかりました。(マニュアルを見直しました。:-))
5. countは配列数を毎回数えてる これを知ったのは、Pukiwkiのクリーンアップの記事を見たときなのですが。 countは配列の件数を数えるらしいので次のような書き方に直したほうが無難です。
count()が実行毎に件数を数えるわけではないのはエントリで触れられているのですが、このtips自体が無意味なわけではありません。
count()の値をループ前に取得しておくのとループ内で呼ぶのとでベンチマークを取ってみると以下のようにやはり差が出ました。
※ソースはodzさんのを編集させていただきました。
< ?php
require_once 'Benchmark/Timer.php';
function test_count_loop($array, $iterate) {
for ($j = 0; $j < $iterate; $j++) {
$n = 0;
$count = count($array);
for ($i = 0; $i < $count; $i++) {
$n = $array[$i];
}
}
}
function test_count_loop_count($array, $iterate) {
for ($j = 0; $j < $iterate; $j++) {
$n = 0;
for ($i = 0; $i < count($array); $i++) {
$n = $array[$i];
}
}
}
function test_foreach($array, $iterate) {
for ($j = 0; $j < $iterate; $j++) {
foreach ($array as $i) {
$n = $i;
}
}
}
$a = range(0, 9999);
$b = range(0, 9999);
$c = range(0, 9999);
$timer = new Benchmark_Timer;
$timer->start();
test_count_loop($a, 1000);
$timer->setMarker('count_loop');
test_count_loop_count($b, 1000);
$timer->setMarker('count_loop_count');
test_foreach($c, 1000);
$timer->setMarker('test_foreach');
$timer->stop();
$timer->display();
?>
■PHP 4.4.4 (cli) --------------------------------------------------------------- count_loop 1161943146.86602700 10.211816072464 34.91% --------------------------------------------------------------- count_loop_count 1161943160.60222800 13.736200809479 46.95% --------------------------------------------------------------- test_foreach 1161943165.90888800 5.3066601753235 18.14% --------------------------------------------------------------- ■PHP 5.1.6 (cli) --------------------------------------------------------------- count_loop 1161943103.61103900 2.0879490375519 24.69% --------------------------------------------------------------- count_loop_count 1161943108.41284200 4.80180311203 56.79% --------------------------------------------------------------- test_foreach 1161943109.97873100 1.5658888816833 18.52% ---------------------------------------------------------------
以前はforよりforeachの方が速い遅いという話を聞いた事があったのですが、いつのまにかforeachの方が速くなってますね。普段は多少のオーバーヘッドは気にせずforeachを使っていたのですが、これで大手を振って使えます。;-)
- Newer: Yahoo!でsymfonyを採用
- Older: CakePHP bake2タスクを作る
トラックバック:0
- このエントリーのトラックバックURL
- /blog/2006/10/php_source_tips.html/trackback
- Listed below are links to weblogs that reference
- PHPのちょっとしたコツ from Shin x blog

