2010-10-28 51 views
7

嘿那裏。今天我寫了一個小型的基準測試腳本來比較複製變量的性能與創建對它們的引用。我期待的是,例如創建對大數組的引用會比複製整個數組慢得多。這裏是我的基準代碼:(!)PHP性能:複製與參考

<?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花了大約20倍,只要recursiveCopy。

有人可以解釋這個PHP行爲嗎?

+0

除了不正確的遞歸,爲什麼麻煩遞歸呢?爲什麼不在每次迭代之後設置一個for循環並取消設置變量(與遞歸調用相比,FAR的開銷更小,而且不會消耗掉所有的內存)......但最終的差距會非常小在99.9999%的情況下,使用語義上適當的賦值(參考,你需要一個,在你不需要的地方是正常的)而不是嘗試微觀優化更合理。 – ircmaxell 2010-10-28 14:04:34

+0

這不是關於試圖優化的東西,我只是好奇。而且我使用遞歸,因爲我不想解除變量和循環等等。寫遞歸的速度要快得多,而且我並不關心開銷,因爲它們在兩者中都是相同的。 – fresskoma 2010-10-28 14:13:39

+0

只需避免大型陣列獲得良好的性能。就這樣。 – 2010-10-28 14:28:50

回答

16

PHP很可能會爲其數組實現copy-on-write,這意味着當您「複製」一個數組時,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); 
} 
+0

另一個有關PHP學習的有趣的事情。謝謝:) – fresskoma 2010-10-28 14:11:34

3

在recursiveReference中你調用了recursiveCopy ...這沒有任何意義,在這種情況下,你只需要調用recursiveReference一次。更正您的代碼,再次提交基準測試並返回新的結果。

此外,我不認爲它是遞歸做這個基準的有用。一個更好的解決方案是在一個循環中調用一個函數1000次 - 直接調用一次數組,然後使用該數組的一個引用。

+0

對不起,我真的不知道那是怎麼回事,那是一個複製並粘貼wtf ...在我運行的版本中,那不是那裏。 – fresskoma 2010-10-28 14:10:28

0
  1. 在recursiveReference()函數調用recursiveCopy()函數。這是你真正打算做什麼?
  2. 你對$ foo變量什麼都不做 - 可能它應該用於進一步的方法調用?
  3. 通過引用傳遞變量通常應該在傳遞大對象時保存堆棧內存。
0

recursiveReference正在調用recursiveCopy。 這不一定會損害性能,但這可能不是你想要做的。

不確定爲什麼性能會變慢,但它並不反映您嘗試創建的度量。

1

一般來說,在PHP中,通過引用進行調用並不是出於性能方面的原因;這是你出於功能原因而做的事 - 即因爲你實際上希望被引用的變量被更新。

如果您沒有通過引用調用的功能原因,那麼您應該堅持使用常規參數傳遞,因爲PHP以這種方式高效地處理事情。

(這麼說,正如其他人所指出的那樣,你的示例代碼是不完全做你想的那樣;))

+0

這是試圖衡量指定分配與價值分配。調用任何東西並不真正進入圖片(除了測試是遞歸的)否則+1 – ircmaxell 2010-10-28 14:06:22

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 

正如你可以看到有由基準分配,直到你真正寫副本,即當沒有顯著的性能差異也有功能差異。