8

ちょっと、そこ。今日、私は変数のコピーと変数への参照の作成のパフォーマンスを比較するための小さなベンチマークスクリプトを作成しました。たとえば、大きな配列への参照の作成は、配列全体をコピーするよりも大幅に遅くなると予想していました。これが私のベンチマークコードです:

<?php
    $array = array();

    for($i=0; $i<100000; $i++) {
        $array[] = mt_rand();
    }

    function recursiveCopy($array, $count) {
        if($count === 1000)
            return;

        $foo = $array;
        recursiveCopy($array, $count+1);
    }

    function recursiveReference($array, $count) {
        if($count === 1000)
            return;

        $foo = &$array;
        recursiveReference($array, $count+1);
    }

    $time = microtime(1);
    recursiveCopy($array, 0);
    $copyTime = (microtime(1) - $time);
    echo "Took " . $copyTime . "s \n";


    $time = microtime(1);
    recursiveReference($array, 0);
    $referenceTime = (microtime(1) - $time);
    echo "Took " . $referenceTime . "s \n";

    echo "Reference / Copy: " . ($referenceTime / $copyTime);

私が得た実際の結果は、recursiveReferenceがrecursiveCopyの約20倍(!)かかったということでした。

誰かがこのPHPの動作を説明できますか?

4

6 に答える 6

17

PHPは、配列にコピーオンライトを実装する可能性が非常に高いです。つまり、配列を「コピー」すると、コピーの1つを変更して変数が参照できなくなるまで、PHPはメモリを物理的にコピーするすべての作業を実行しません。同じ内部表現。

recursiveCopyしたがって、関数は実際にはオブジェクトをコピーしないため、ベンチマークには根本的な欠陥があります。もしそうなら、あなたはすぐにメモリを使い果たしてしまうでしょう。

これを試してください:配列の要素に割り当てることにより、PHPに実際にコピーを作成させることができます。再帰関数が最大の深さに達するまで、どのコピーもスコープ外に出ない(そしてガベージコレクションされない)ため、メモリがすぐに不足することがわかります。

function recursiveCopy($array, $count) {
    if($count === 1000)
        return;

    $foo = $array;
    $foo[9492] = 3; // Force PHP to copy the array
    recursiveCopy($array, $count+1);
}
于 2010-10-28T13:46:29.767 に答える
3

recursiveReferenceでは、recursiveCopyを呼び出しています...これは意味がありません。この場合、recursiveReferenceを1回だけ呼び出しています。コードを修正し、ベンチマークを再度実行して、新しい結果を返します。

さらに、これを再帰的に行うことはベンチマークにとって有用ではないと思います。より良い解決策は、ループ内で関数を1000回呼び出すことです。1回は配列を直接使用し、もう1回はその配列への参照を使用します。

于 2010-10-28T13:44:41.707 に答える
3

パフォーマンス上の理由から、参照によって変数を割り当てたり渡したりする必要はありません(したがって、割り当てるべきではありません)。PHPはそのような最適化を自動的に行います。

これらの自動最適化のために、実行したテストに欠陥があります。代わりに次のテストを実行しました。

<?php
for($i=0; $i<100000; $i++) {
    $array[] = mt_rand();
}

$time = microtime(1);
for($i=0; $i<1000; $i++) {
    $copy = $array;
    unset($copy);
}
$duration = microtime(1) - $time;
echo "Normal Assignment and don't write: $duration<br />\n";

$time = microtime(1);
for($i=0; $i<1000; $i++) {
    $copy =& $array;
    unset($copy);
}
$duration = microtime(1) - $time;
echo "Assignment by Reference and don't write: $duration<br />\n";

$time = microtime(1);
for($i=0; $i<1000; $i++) {
    $copy = $array;
    $copy[0] = 0;
    unset($copy);
}
$duration = microtime(1) - $time;
echo "Normal Assignment and write: $duration<br />\n";

$time = microtime(1);
for($i=0; $i<1000; $i++) {
    $copy =& $array;
    $copy[0] = 0;
    unset($copy);
}
$duration = microtime(1) - $time;
echo "Assignment by Reference and write: $duration<br />\n";
?>

これは出力でした:

//Normal Assignment without write: 0.00023698806762695
//Assignment by Reference without write: 0.00023508071899414
//Normal Assignment with write: 21.302103042603
//Assignment by Reference with write: 0.00030708312988281

ご覧のとおり、実際にコピーに書き込むまで、つまり機能的な違いがある場合は、参照による割り当てに大きなパフォーマンスの違いはありません。

于 2011-07-26T16:31:42.727 に答える
1

一般的にPHPで言えば、参照による呼び出しは、パフォーマンス上の理由から行うことではありません。これは、機能上の理由で行うことです。つまり、参照される変数を実際に更新する必要があるためです。

参照によって呼び出す機能的な理由がない場合は、通常のパラメーターの受け渡しを使用する必要があります。これは、PHPがその方法で完全に効率的に処理するためです。

(とはいえ、他の人が指摘しているように、あなたのサンプルコードはとにかくあなたが思っていることを正確に行っていません;))

于 2010-10-28T13:55:35.267 に答える
0
  1. recursiveReference()関数では、recursiveCopy()関数を呼び出します。それはあなたが本当に意図したことですか?
  2. $ foo変数では何もしません-おそらくそれはさらなるメソッド呼び出しで使用されることになっていますか?
  3. 参照によって変数を渡すと、通常、大きなオブジェクトを渡す場合にスタックメモリを節約できます。
于 2010-10-28T13:47:18.250 に答える
0

recursiveReferenceはrecursiveCopyを呼び出しています。それが必ずしもパフォーマンスに悪影響を与えるわけではありませんが、おそらくそれはあなたがやろうとしていることではありません。

パフォーマンスが遅い理由はわかりませんが、実行しようとしている測定値は反映されていません。

于 2010-10-28T13:47:46.690 に答える