13

私の調査によると、forループは PHP で最速の反復構造であると信じています... より明確にするために、次のうちどれがより高速だと思いますか?

例1

for ($i = 0; $i < count($myLargeArray); $i++ ) {
    echo myLargeArray[$i];
}

例 2

$count = count($myLargeArray);
for ($i = 0; $i < $count; $i++ ) {
    echo myLargeArray[$i];
}

私の論理は、例 1 の各反復で、各反復で myLargeArray の長さにアクセスすることは、例 2 のように単純な整数値にアクセスするよりも計算コストがかかるということです。あれは正しいですか?

4

6 に答える 6

15

count()最初の方法は、ループの反復ごとに関数を呼び出す必要があるため、処理が遅くなります。メソッド自体は非常に高速ですが、count()関数の呼び出しにはまだオーバーヘッドがいくらかあります。ループの外に移動することで、いわゆる「ループ不変コード モーション」、または場合によっては「巻き上げ」を実行します。

このような、学ぶのが興味深い最適化のファミリー全体があります。

そうは言っても、これについて強調することはめったにありません。ここの例では、出力をエコーする I/O は、おそらく「最適化」によって節約した値の 10 倍です。そして、ループ内で何か他のことを行うと、最適化の意味はますます少なくなります。

濡れた毛布になるのは嫌いですが、コードの 90% 以上では、パフォーマンスは問題になりません。特に、そもそも 90% 以上の I/O を占める Web アプリケーションについて話す場合はなおさらです。

それでも、コードに問題があると思われる場合は、次のことを行う必要があります。

  1. 最適化が必要なユースケースを決定する
  2. コードのパフォーマンスを測定する
  3. ボトルネックを見つける
  4. 改善できる領域を特定し、時間をかけて改善する価値があるかどうかを判断します。
  5. コードを変更する
  6. 手順 2 に戻る

ほとんどの場合、コードをいじる代わりに、キャッシュ戦略とデータベースの最適化 (別の手段による I/O の最適化) を改善する必要があることに気付くでしょう。

于 2012-11-20T04:52:39.330 に答える
2

例 2. 繰り返しごとに要素を数えないでください。

更新: 値が事前に計算されていると言われました: nNumOfElements specifies how many values are currently stored in the array. This is also the number thatcount($array)returns.

count()この関数は、数マイクロ秒とクロックサイクルを浪費する以外は文字通り何もしないように思えます(アセンブラを知っている人にとっては)。

こちらをお読みください: PHP の内部配列実装の理解 (PHP 開発者向けの PHP のソース コード - パート 4)

おそらくあなたは試すことができますforeach range

foreach (range(0, (count(array)) as $number) {
    echo $number;
}
于 2012-11-20T03:48:05.117 に答える
2

この場合の最速の構造は、実際には foreach ループです。

foreach($myLargeArray as $element) {
    echo $element;
} 

foreach() は、常に終了するという点でも優れていますが、for() を使用すると、入力ミスにより無限ループが発生する可能性があります。

于 2012-11-20T04:46:55.343 に答える
0

明らかに例の方が遅いです。条件$i < count($myLargeArray)は反復ごとに評価されるため、配列は複数回カウントされます。

http://www.phpbench.com/で、このベンチマークと他のベンチマークを確認してください。

編集:彼らはソースコードを調べました、そしてそれは事前計算されています。

ただし、これらの複数の関数呼び出しで処理時間が無駄になります。そのため、パフォーマンスが低下します。配列は複数回「カウント」されます。

于 2012-11-20T03:48:42.253 に答える
0

そこで、いくつかの実数を取得するために、いくつかのことを実際に定量化することにしました。これがベースライン コードで、100000 個の整数の大きな配列を構築するループです。

$x = array();
for ($idx=0; $idx<100000; $idx++)
    $x[] = $idx;

平均実行時間: 85 ミリ秒。これには、PHP を起動し、プログラムを解析し、実行して終了するまでの時間が含まれます。ここで、配列を反復処理する別のループを追加します。

for ($idx=0; $idx<count($x); $idx++) { 
    ;
}

平均実行時間: 105 ミリ秒。85 ミリ秒のセットアップ時間を差し引くと、100,000 個のメンバー配列を反復するのに 20 ミリ秒しかかからないことがわかります。

ここで、ループ不変コード モーションを追加します。

$m = count($x);
for($idx=0; $idx<$m; $idx++) { 
    ;
}

平均実行時間: 90 ミリ秒。

一方で、この節約は莫大です。これは、20 ミリ秒ではなく 5 ミリ秒のループ反復時間です。したがって、75% の節約であると主張できます。

一方、15ミリ秒です。とてつもなく大きな配列では、ほとんどの人が気づくよりも短い時間です。

しかし、これは何もしない配列です。いくつかのデータを出力するとどうなるか見てみましょう:

$m = count($x);
for ($idx=0; $idx<$m; $idx++) { 
    echo $idx;
}

現在、実行時間は 200 ミリ秒です。おっと、ループ インデックスだけを出力しました。配列の内容も出力しませんでした。

それはばかげています。ルック カウンターだけでなく、配列の内容をエコーするようにプログラムを再度変更しましょう。

$m = count($x);
for ($idx=0; $idx<$m; $idx++)
    echo $x[$idx];

新しい実行時間は 212 ミリ秒です。そのため、ループ カウンターを単にエコーするよりも、配列の内容にアクセスしてエコーするのに 5% 長くかかりました。

誰かの以前の提案を取り上げて、ループを展開してみましょう。私は過去にこれを C/C++ で非常に効果的に使用しました。

$m = count($x);
for ($idx=0; $idx<$m; $idx+=5) {
    echo $x[$idx];
    echo $x[$idx+1];
    echo $x[$idx+2];
    echo $x[$idx+3];
    echo $x[$idx+4];
}

今、私たちは話しています!206 ミリ秒まで低下しています。ちょっと待って、これは面白くないコードの約 3% の改善です。そして、出力はひどく見えます。空白などのない数字の文字列です。

ループのアンローリングを取り除き、出力をもう少し良くしましょう:

$m = count($x);
for ($idx=0; $idx<$m; $idx++)
    echo "{$x[$idx]}\n";

実行時間は 400 ミリ秒です。は。フォーマットを取得するだけでも、(比較的言えば) 余分な時間がかかります。文字列の置換を使用すると、コストがかかる可能性があります。代わりに文字列連結を試してみましょう:

$m = count($x);
for ($idx=0; $idx<$m; $idx++)
    echo $x[$idx] . "\n";

新しい時間は 390 ミリ秒です。少し良く。数値を改行の代わりにスペースで区切ってみましょう:

$m = count($x);
for ($idx=0; $idx<$m; $idx++)
    echo $x[$idx] . " ";

おっと、224 ミリ秒に戻っています。右に!しかし何が起こった?さて、私はこれをすべてUnix端末で実行していますが、数値を別々の行に出力するのは、ラップする1行にすべてを出力するよりも単純に遅くなります。

言い換えれば、端末プログラムのスクロールの速度は、私たちが行った何よりも大きな影響を及ぼします。

于 2012-11-20T05:56:56.397 に答える
0

最も速いループは、ループをアンロールすることです。一部のコード エディターでは、PHP エディターではなく、特別なマクロでこれをサポートしているため、コピーして貼り付ける必要はありません。

于 2012-11-20T05:12:51.927 に答える