| PHP Developer Forum Hier habt ihr die Möglichkeit, eure Skriptprobleme mit anderen Anwendern zu diskutieren. Seid so fair und beantwortet auch Fragen von anderen Anwendern. Dieses Forum ist sowohl für ANFÄNGER als auch für PHP-Profis! Post your PHP questions here! |
 |
|

23-06-2009, 22:26
|
|
phpMorpheus2
Registrierter Benutzer
|
|
Registriert seit: Apr 2007
Beiträge: 646
|
|
Ausweg aus dem Referenzdilemma
Hallo.
Nocheinmal wende ich man an Euch
Ich verstehe folgende Aussage nicht:
Zitat:
Ausweg aus dem Referenzdilemma
Wie also kann man sicherstellen, daß eine Referenz auch als solche behandelt wird? In PHP gibt es dafür spezielle Syntax, bei der das Kaufmanns-Und den Referenz-Charakter einer Zuweisung oder einer Variablen-Betrachtung anzeigt:
PHP-Code:
$a = &$b;
$c =& $a;
$c = 42;
Die beiden obigen Zuweisung bewirken gleichermaßen, daß jeweils beide Variablen nicht nur denselben Wert haben, sondern auch eine Wertänderung einer der Variablen automatisch die exakt selbe Wertänderung der anderen Variablen bewirkt. Die letzte Anweisung bewirkt folglich, daß alle drei Variablen den Wert 42 annehmen.
Auch bei Funktions- und Methodenaufrufen wird normalerweise Kopiersemantik angewandt. Um explizit Referenzsemantik zu fordern, stellt man hierbei bei einer Funktions-/Methodendefinition einer zu referenzierenden Parameter-Variablen ein Kaufmanns-Und voran:
PHP-Code:
function Plus($var) { $var++; }
function Minus(&$var) { $var--; }
$var = 42;
Plus($var);
Minus($var);
echo $var;
Die Ausgabe obigen Codes ist natürlich 41, da nur die Funktion Minus den Wert der Variablen $var ändert. In Java wäre das nicht so offensichtlich, da hier u.U. eine Klassenvariable namens var existieren könnte, die implizit gemeint sein könnte. In PHP müßte man dann aber $this->var schreiben.
Unter 20.7.2 findet sich noch eine Übung zu komplexeren Anwendungen der Referenzsemantik.
|
Wovon ist gerade die Rede?
Obiger Code:
PHP-Code:
$a = &$b;
$c =& $a;
$c = 42;
bezieht sich auf eine Klasse, richtig?
Wenn ja, warum sollte $a = 42 sein?
Sie wird später definiert.
Also müssen die Variablen ja Objekte sein die angelegt wurden.
Folglich nehmen Objekte, auch wenn sie vorher definiert werden,
die Werte an, welche durch andere Objekte springen können (wenn definiert)
und so einen Wert ändern, trotz das diese vorher definiert wurden ?!?
Oder wie jetzt?
Das bringt mich zum Grübeln...
|

23-06-2009, 22:31
|
wahsaga
 Moderator
|
|
Registriert seit: Sep 2001
Beiträge: 24.486
|
|
Zitat:
Zitat von phpMorpheus2
Obiger Code:
PHP-Code:
$a = &$b;
$c =& $a;
$c = 42;
bezieht sich auf eine Klasse, richtig?
|
Nein.
Zitat:
Wenn ja, warum sollte $a = 42 sein?
Sie wird später definiert.
|
Gerade deshalb, weil $c eine Referenz auf $a darstellt.
Zum Lesen: http://www.php.net/manual/de/language.references.php
__________________
I don't believe in rebirth. Actually, I never did in my whole lives.
|

23-06-2009, 23:27
|
|
combie
PHP Expert
|
|
Registriert seit: May 2006
Beiträge: 2.925
|
|
Zitat:
|
Das bringt mich zum Grübeln...
|
Das hat nix mit "Objecte" zu tun. Objecte werden seit PHP5 immer per Referenz übergeben. Es sei denn du Clonst sie.
|

24-06-2009, 00:20
|
|
phpMorpheus2
Registrierter Benutzer
|
|
Registriert seit: Apr 2007
Beiträge: 646
|
|
Danke. Jetzt habe ich es verstanden.
Jede Referenz verändert also die ursprüngliche Variable.
|

24-06-2009, 00:47
|
|
phpMorpheus2
Registrierter Benutzer
|
|
Registriert seit: Apr 2007
Beiträge: 646
|
|
Ich hatte nicht gewusst,
das PHP4 den Zuweisungsoperator =& verwendet,
PHP5 jedoch nur = zum referenzieren benötigt.
Daher hat mich =& und = verwirrt.
Jetzt weiß ich, dass es die selben Zuweisungsoperatoren sind, = jedoch neuer ist.
|

24-06-2009, 00:51
|
wahsaga
 Moderator
|
|
Registriert seit: Sep 2001
Beiträge: 24.486
|
|
Zitat:
Zitat von phpMorpheus2
Ich hatte nicht gewusst,
das PHP4 den Zuweisungsoperator =& verwendet,
PHP5 jedoch nur = zum referenzieren benötigt.
Daher hat mich =& und = verwirrt.
Jetzt weiß ich, dass es die selben Zuweisungsoperatoren sind, = jedoch neuer ist.
|
Das ist so allgemeingültig falsch.
Objekte werden unter PHP 5 immer per Referenz übergeben.
Für Skalare gilt das natürlich so nicht. Da braucht es immer noch das &, wenn mit Referenzen gearbeitet werden soll.
__________________
I don't believe in rebirth. Actually, I never did in my whole lives.
|

24-06-2009, 09:48
|
 |
mermshaus
Registrierter Benutzer
|
|
Registriert seit: Jun 2009
Beiträge: 165
|
|
Semi-Offtopic, weil es hier irgendwie passt:
Ein Array wird ebenfalls standardmäßig per Referenz übergeben, wenn sein Inhalt im Funktionskörper nicht verändert wird. Es scheint sich sogar negativ auf die Geschwindigkeit auszuwirken, dem Array (das nicht verändert wird) in der Funktionssignatur ein & voranzustellen.
Das ist natürlich eine nachvollziehbare Optimierung, aber weiß jemand, wo man das nachlesen kann?
Gruß Marc
|

24-06-2009, 09:59
|
unset
 Moderator
|
|
Registriert seit: Jan 2007
Ort: Düsseldorf
Beiträge: 3.778
|
|
Zitat:
Zitat von mermshaus
Ein Array wird ebenfalls standardmäßig per Referenz übergeben, wenn sein Inhalt im Funktionskörper nicht verändert wird.
|
Wie willst du das feststellen, wenn diese Referenz verfällt, sobald sich etwas ändert?
|

24-06-2009, 10:03
|
|
combie
PHP Expert
|
|
Registriert seit: May 2006
Beiträge: 2.925
|
|
Zitat:
|
Es scheint sich sogar negativ auf die Geschwindigkeit auszuwirken, dem Array (das nicht verändert wird) in der Funktionssignatur ein & voranzustellen.
|
Unglaublich, aber wahr!
Testcode:
PHP-Code:
function test1(Array $array)
{
return count($array);
}
function test2(Array &$array)
{
return count($array);
}
$array = range(1,300000);
$start = microtime(true);
for ($i=0;$i<20;$i++)
{
test1($array);
}
$used = microtime(true)-$start;
echo sprintf('%01.5f <hr>',$used);
$start = microtime(true);
for ($i=0;$i<20;$i++)
{
test2($array);
}
$used = microtime(true)-$start;
echo sprintf('%01.5f <hr>',$used);
|

24-06-2009, 10:32
|
 |
mermshaus
Registrierter Benutzer
|
|
Registriert seit: Jun 2009
Beiträge: 165
|
|
Zitat:
Zitat von unset
Wie willst du das feststellen, wenn diese Referenz verfällt, sobald sich etwas ändert?
|
Ja, das hätte ich nicht so absolut ausdrücken sollen. Das ist die Vermutung aus den Benchmark-Zeiten.
Bei Strings ist es auch so:
Edit: Okay, vielleicht jetzt nicht gerade *die* Entdeckung.
PHP-Code:
ini_set('memory_limit', '64M');
function test1($s) { return strlen($s); }
function test2(&$s) { return strlen($s); }
$s = str_repeat(' ', 300000);
$start = microtime(true); for ($i=0;$i<20;$i++) { test1($s); } $used = microtime(true)-$start; echo sprintf('%01.5f <hr>',$used);
$start = microtime(true); for ($i=0;$i<20;$i++) { test2($s); } $used = microtime(true)-$start; echo sprintf('%01.5f <hr>',$used);
|

24-06-2009, 10:59
|
unset
 Moderator
|
|
Registriert seit: Jan 2007
Ort: Düsseldorf
Beiträge: 3.778
|
|
Zitat:
Zitat von mermshaus
Ja, das hätte ich nicht so absolut ausdrücken sollen. Das ist die Vermutung aus den Benchmark-Zeiten.
|
Mir ging es eher um die Aussage, dass Arrays als Referenz übergeben werden, solange sich nichts an ihnen ändert. Wie hast du das herausgefunden?
|

24-06-2009, 11:09
|
 |
onemorenerd
 Moderator
|
|
Registriert seit: Mar 2005
Ort: Berlin
Beiträge: 9.481
|
|
Das nennt sich Copy-On-Write und ist eine gängige Technik. Das Anlegen einer Kopie kostet Zeit und Speicher. Deshalb wird darauf verzichtet bis zum ersten Schreibzugriff.
|

24-06-2009, 11:39
|
 |
mermshaus
Registrierter Benutzer
|
|
Registriert seit: Jun 2009
Beiträge: 165
|
|
@unset:
Um die Aussage ging es mir auch, denn genau weiß (wusste  ) ich nicht, ob das so ist, weil ich noch keine Dokumentation gefunden habe, die das bestätigt. Es ist nur der sinnvolle Schluss aus den Benchmarkergebnissen.
Bemerkt habe ich das, als ich einen Absatz zum Thema Codeoptimierung durch Übergabe von primitiven Datentypen (dazu habe ich das Array gezählt) als Referenz schreiben wollte und quasi den Benchmark von combie gebaut habe, um zu gucken, wie unglaublich viel Zeit man spart.
@onemorenerd:
Hast du auch eine Erklärung, wieso die Funktion test2() in combies Benchmark so lange braucht?
Kann man Primitive etwa gar nicht als "echte" Referenzen übergeben? Kopiert PHP die entsprechenden Daten intern trotzdem beim Aufruf hin und bei Funktionsende wieder zurück und gaukelt die entsprechende Funktionalität bloß vor? PHP: Passing by Reference - Manual
Das wäre eine krasse Bildungslücke.
Edit: Heißen die deshalb Primitive? 
Edit2: Habe mir nie viel hierbei gedacht: "References in PHP are a means to access the same variable content by different names. They are not like C pointers; instead, they are symbol table aliases."
Geändert von mermshaus (24-06-2009 um 11:51 Uhr)
Grund: Au wei au wei
|

24-06-2009, 13:53
|
 |
onemorenerd
 Moderator
|
|
Registriert seit: Mar 2005
Ort: Berlin
Beiträge: 9.481
|
|
Diesen extremen Unterschied in combies Benchmark zweifle ich an. Hier mal ein anderer, bei dem die Tests nicht sequentiell sondern durch einzelne Requests und dadurch unter identischen Bedingungen ausgeführt werden. usleep() verwende ich als noop um zu verhindern, dass der Parser die Funktion oder den If-Block wegoptimiert.
PHP-Code:
<?php
function t_ ($a) { usleep(0); } function t_ref (&$a) { usleep(0); } function t_hint (array $a) { usleep(0); } function t_hint_ref (array &$a) { usleep(0); } function t_read ($a) { if ($a) usleep(0); } function t_ref_read ($a) { if ($a) usleep(0); } function t_hint_read (array $a) { if ($a) usleep(0); } function t_hint_ref_read (array &$a) { if ($a) usleep(0); } function t_write ($a) { usleep(0); $a = 0; } function t_ref_write ($a) { usleep(0); $a = 0; } function t_hint_write (array $a) { usleep(0); $a = 0; } function t_hint_ref_write (array &$a) { usleep(0); $a = 0; } function t_read_write ($a) { if ($a) usleep(0); $a = 0; } function t_ref_read_write ($a) { if ($a) usleep(0); $a = 0; } function t_hint_read_write (array $a) { if ($a) usleep(0); $a = 0; } function t_hint_ref_read_write (array &$a) { if ($a) usleep(0); $a = 0; }
header('Content-Type: text/plain'); if (isset($_GET['func']) && substr($_GET['func'], 0, 2) == 't_' && isset($_GET['size']) && isset($_GET['runs'])) { $orig = range(1, (int)$_GET['size']); $start = microtime(true); for ($i = 0; $i < (int)$_GET['runs']; $i++) { $copy = $orig; $_GET['func']($copy); } echo sprintf("%01.5f µs - %d Bytes", microtime(true) - $start, memory_get_peak_usage(true)); } else { $url = 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['PHP_SELF']; $runs = isset($_GET['runs']) ? (int)$_GET['runs'] : 1000; $size = isset($_GET['size']) ? (int)$_GET['size'] : 1000; $funcs = get_defined_functions();
echo "runs: $runs\nsize: $size\n";
foreach ($funcs['user'] as $func) { echo file_get_contents($url.'?func='.$func.'&size='.$size.'&runs='.$runs)." - $func\n"; } }
Code:
runs: 10000
size: 1000
0.09862 µs - 262144 Bytes - t_
0.75749 µs - 262144 Bytes - t_ref
0.09803 µs - 262144 Bytes - t_hint
0.75746 µs - 262144 Bytes - t_hint_ref
0.09851 µs - 262144 Bytes - t_read
0.09817 µs - 262144 Bytes - t_ref_read
0.09805 µs - 262144 Bytes - t_hint_read
0.75787 µs - 262144 Bytes - t_hint_ref_read
0.09879 µs - 262144 Bytes - t_write
0.09921 µs - 262144 Bytes - t_ref_write
0.09975 µs - 262144 Bytes - t_hint_write
0.76339 µs - 262144 Bytes - t_hint_ref_write
0.09973 µs - 262144 Bytes - t_read_write
0.10127 µs - 262144 Bytes - t_ref_read_write
0.10139 µs - 262144 Bytes - t_hint_read_write
0.76083 µs - 262144 Bytes - t_hint_ref_read_write
Mein Fazit:
Type Hints allein kosten fast nichts.
Referenzen allein kosten fast nichts.
Type Hints auf Referenzen sind teuer.
Warum das so ist kann ich aus dem Stand nicht sagen. Wenn du es unbedingt wissen willst, schau in den Source Code des Interpreters. Mir ist der Unterschied zu gering, ich mach mir da keinen Kopf.
Übrigens kannst du bei diesem Benchmark mal langsam Size hoch schrauben und wirst sehen, dass Type Hints auf Referenzen auch mehr Speicher brauchen.
http://derickrethans.nl/files/phparc...es-article.pdf Unbedingt lesen!
|

24-06-2009, 15:33
|
 |
mermshaus
Registrierter Benutzer
|
|
Registriert seit: Jun 2009
Beiträge: 165
|
|
Edit: Kommando zurück. Im Benchmark-Code war noch eine Zeile $copy = $orig, die ich übersehen hatte. Das heißt, es existieren bereits zwei Bezeichner, die auf denselben Inhalt zeigen. Wenn dann auf einen der beiden noch eine Referenz gesetzt wird, dann wird wohl kopiert, iirc (siehe Artikel). Jedenfalls sind dann die Benchmark-Zeiten gleich ganz anders, wenn die entsprechende Zeile auskommentiert und $orig übergeben wird.
Den Artikel hatte ich auch gefunden und ich denke mal, Figure 7 sollte unserem Problem hier entsprechen. Allerdings sehe ich da nicht, wo bei Aufruf der Inhalt der per Referenz an die Funktion übergebenen Variable kopiert wird (oder ähnliches). Das sieht für mich so aus, als würde da nur ein zweiter "Zeiger" auf denselben Speicherinhalt gesetzt. Wo geht da die Zeit verloren (siehe unten)?
Vielen Dank für den Benchmark! Der sieht von Aufbau sehr gelungen aus. Allerdings hast du in den Funktionsdeklarationen ein paar Referenzen vergessen und ich glaube außerdem, dass das Schreiben eher mit $a[0] = 0; geprüft werden sollte, da er sonst nicht zu "copy on write" gezwungen ist. (Könnte zudem sprintf die memory_get_peak_usage bei kleiner $size beeinflussen? Das habe ich jetzt nicht getestet.)
Mit den beschriebenen Änderungen ergibt sich:
Code:
runs: 10000
size: 1000
0.70719 µs - 262144 Bytes - t_
2.92888 µs - 262144 Bytes - t_ref
0.62768 µs - 262144 Bytes - t_hint
2.35741 µs - 262144 Bytes - t_hint_ref
0.64666 µs - 262144 Bytes - t_read
2.45519 µs - 262144 Bytes - t_ref_read
0.64916 µs - 262144 Bytes - t_hint_read
2.31166 µs - 262144 Bytes - t_hint_ref_read
2.33536 µs - 262144 Bytes - t_write
2.43142 µs - 262144 Bytes - t_ref_write
2.43154 µs - 262144 Bytes - t_hint_write
2.25035 µs - 262144 Bytes - t_hint_ref_write
2.05961 µs - 262144 Bytes - t_read_write
2.41068 µs - 262144 Bytes - t_ref_read_write
2.45428 µs - 262144 Bytes - t_hint_read_write
2.45996 µs - 262144 Bytes - t_hint_ref_read_write
Wenn ich keinen Unsinn gemacht habe, sind die Referenzen also immer langsam.
Habe mir ein wenig was abgeguckt und noch mal ein anderes Beispiel versucht:
PHP-Code:
header('content-type: text/plain');
$array = range(1, 30000); echo memory_get_peak_usage(), " - start\n";
if (is_array($array)) { usleep(0); } echo memory_get_peak_usage(), " - is_array(original)\n";
$a = &$array; echo memory_get_peak_usage(), " - copy to ref\n";
$a[0] = 0; echo memory_get_peak_usage(), " - write ref\n";
if ($a[0]) { usleep(0); } echo memory_get_peak_usage(), " - read ref\n";
if (is_array($a)) { usleep(0); } echo memory_get_peak_usage(), " - is_array(ref)\n";
Ausgabe:
Code:
3187556 - start
3187660 - is_array(original)
3187724 - copy to ref
3187724 - write ref
3187724 - read ref
5118904 - is_array(ref)
Ich kann mir noch keinen Reim drauf machen, weiß aber aktuell auch nicht mehr, was sich eigentlich zu messen lohnt.
Edit: http://bugs.php.net/bug.php?id=34540 *schulterzuck*
Vorläufiges Fazit: Es ist vermutlich immer eine sehr ineffiziente Idee ist, Arrays als Referenz zu übergeben.
Geändert von mermshaus (24-06-2009 um 15:52 Uhr)
Grund: Sorry für das Chaos
|
|
Aktive Benutzer in diesem Thema: 1 (Registrierte Benutzer: 0, Gäste: 1)
|
|
|
| Themen-Optionen |
|
|
| Thema bewerten |
|
|
Forumregeln
|
Es ist Ihnen nicht erlaubt, neue Themen zu verfassen.
Es ist Ihnen nicht erlaubt, auf Beiträge zu antworten.
Es ist Ihnen nicht erlaubt, Anhänge hochzuladen.
Es ist Ihnen nicht erlaubt, Ihre Beiträge zu bearbeiten.
HTML-Code ist aus.
|
|
|
|
PHP News
|