2010-10-28 12 views
7

Hey da. Heute habe ich ein kleines Benchmark-Skript geschrieben, um die Leistung des Kopierens von Variablen mit dem Erstellen von Referenzen zu vergleichen. Ich hatte erwartet, dass das Erstellen von Verweisen auf große Arrays beispielsweise wesentlich langsamer wäre als das Kopieren des gesamten Arrays. Hier ist mein Benchmark-Code: (!)PHP-Leistung: Kopieren vs. Referenz

<?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); 

Das eigentliche Ergebnis, das ich bekam, war, dass recursiveReference etwa 20-mal dauerte so lange, wie recursiveCopy.

Kann jemand dieses PHP Verhalten erklären?

+0

Abgesehen von der inkorrekten Rekursion, warum überhaupt rekursiv? Warum nicht einfach eine for-Schleife einrichten und die Variablen nach jeder Iteration aufheben (was FAR weniger Overhead haben wird als ein rekursiver Aufruf und nicht den gesamten Speicher auffressen) ... Aber am Ende wird der Unterschied so klein sein, dass In 99,9999% der Fälle ist es sinnvoller, die semantisch passende Zuweisung zu verwenden (Referenz, wo Sie eine brauchen, normal, wo Sie sie nicht brauchen), anstatt zu versuchen, die Mikro-Optimierung zu verwenden. – ircmaxell

+0

Es ging nicht darum, Dinge zu optimieren, ich war nur neugierig. Und ich benutzte Rekursion, weil ich die Vars und die Schleife usw. nicht aufheben wollte. Es war viel schneller eine Rekursion zu schreiben, und mir war der Overhead egal, weil es in beiden gleich war. – fresskoma

+0

Vermeiden Sie große Arrays, um eine gute Leistung zu erzielen. Das ist alles. –

Antwort

16

PHP wird sehr wahrscheinlich copy-on-write für seine Arrays implementieren, was bedeutet, wenn Sie ein Array "kopieren", macht PHP nicht die ganze Arbeit des physischen Kopierens des Speichers, bis Sie eine der Kopien ändern und Ihre Variablen nicht mehr referenzieren können die gleiche interne Darstellung.

Ihr Benchmarking ist daher grundlegend fehlerhaft, da Ihre recursiveCopy Funktion das Objekt nicht wirklich kopiert; Wenn dies der Fall wäre, würde Ihnen sehr schnell der Speicher ausgehen.

Versuchen Sie Folgendes: Indem Sie einem Element des Arrays zuweisen, erzwingen Sie PHP tatsächlich eine Kopie erstellen. Sie werden feststellen, dass Ihnen der Speicher schnell ausgeht, da keine der Kopien den Gültigkeitsbereich verlässt (und keine Daten gesammelt werden), bis die rekursive Funktion ihre maximale Tiefe erreicht.

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

    $foo = $array; 
    $foo[9492] = 3; // Force PHP to copy the array 
    recursiveCopy($array, $count+1); 
} 
+0

Eine andere interessante Sache über PHP gelernt. Danke :) – fresskoma

3

in recursiveReference Sie rufen recursiveCopy auf ... das macht keinen Sinn, in diesem Fall rufen Sie recursiveReference nur einmal auf. Korrigieren Sie Ihren Code, runden Sie den Benchmark erneut ab und kommen Sie mit Ihren neuen Ergebnissen zurück.

Darüber hinaus glaube ich nicht, dass es für einen Benchmark das rekursiv ist. Eine bessere Lösung wäre, eine Funktion 1000 Mal in einer Schleife aufzurufen - einmal mit dem Array direkt und einmal mit einem Verweis auf dieses Array.

+0

Entschuldigung, ich weiß nicht genau, wie das dahin gekommen ist, das war ein Copy & Paste Wtf ... In der Version, die ich lief, war das nicht da. – fresskoma

0
  1. In recursiveReference() Funktion, die Sie recursiveCopy() Funktion aufrufen. Ist es das, was du wirklich vorhattest?
  2. Sie tun nichts mit $ foo Variable - wahrscheinlich sollte es in weiteren Methoden Aufruf verwendet werden?
  3. Wenn die Variable als Referenz übergeben wird, sollte im Allgemeinen der Stapelspeicher beim Übergeben großer Objekte gespeichert werden.
0

recursiveReference ruft recursiveCopy auf. Nicht das würde die Leistung beeinträchtigen, aber das ist wahrscheinlich nicht das, was Sie versuchen zu tun.

Nicht sicher, warum die Leistung langsamer ist, aber es spiegelt nicht die Messung wieder, die Sie versuchen zu machen.

1

Im Allgemeinen ist das Aufrufen von Referenz in PHP aus Leistungsgründen nicht möglich. Es ist etwas, das Sie aus funktionalen Gründen tun würden - dh, weil Sie tatsächlich möchten, dass die referenzierte Variable aktualisiert wird.

Wenn Sie keinen funktionalen Grund für den Aufruf per Referenz haben, sollten Sie bei der regulären Parameterübergabe bleiben, da PHP die Dinge auf diese Weise effizient verarbeitet.

(das gesagt ist, wie andere haben darauf hingewiesen, Ihr Beispielcode ist nicht genau das, was Sie denken, es ist sowieso;))

+0

Dies versucht zu messen per Referenz vs vs nach Wert zuweisen. Etwas aufrufen geht nicht wirklich ins Bild (außer der Test ist rekursiv). Ansonsten +1 – ircmaxell

3

Sie brauchen nicht zu (und sollte daher nicht) zuweisen oder übergeben Variablen nur durch Leistungsbezug aus Referenz. PHP führt solche Optimierungen automatisch aus.

Der Test, den Sie ausgeführt haben, ist aufgrund dieser automatischen Optimierungen fehlerhaft. In lief den folgenden Test statt:

<?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"; 
?> 

Dies war der Ausgangs:

//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 

Wie Sie bei der Zuordnung von Referenz sehen, dass es keinen signifikanten Unterschied in der Leistung ist, bis Sie auf die Kopie tatsächlich schreiben, dh wenn es gibt auch einen funktionalen Unterschied.