Maximaler Arbeitsspeicherverbrauch mehrerer Funktionen
Normalerweise misst man den aktuellen und maximalen Speicherverbrauch eines Scripts mit den Funktionen memory_get_usage() und memory_get_peak_usage(). Nun habe ich allerdings das Problem dass ich 3 Stellen innerhalb eines Scripts auf den maximalen Speicherverbrauch überprüfen muss, mit nur einem Aufruf. Da es keine Reset-Funktion gibt um den Peak-Verbrauch zurückzusetzen (oder übersehe ich was?) ist das also nicht möglich, bzw. nur möglich wenn die zweite Funktion einen höheren Peak-Verbrauch hat als die erste und die dritte einen höheren als die zweite.
Wie würdet ihr das Problem lösen, ohne 3 Aufrufe des Scripts machen zu müssen, wobei jeweils nur eine Funktion einkommentiert ist? Ich möchte auch nicht die entsprechenden Funktionen mittels pcntl_fork() kurzzeitig in einem Kindprozess starten da es nicht unter Windows funktioniert. Nehmen wir folgendes kurze Snippet als Beispiel:
<?php $result = SumFunctions::viaArraySum(70000); echo "Result 1: $result\n"; echo "Memory Peak 1:".memory_get_peak_usage()."\n"; $result = SumFunctions::summingUpVariable(70000); echo "Result 2: $result\n"; echo "Memory Peak 2:".memory_get_peak_usage()."\n"; $result = SumFunctions::addingPairs(70000); echo "Result 3: $result\n"; echo "Memory Peak 3:".memory_get_peak_usage()."\n"; class SumFunctions { public static function viaArraySum($max) { return array_sum(range(0, $max)); } public static function summingUpVariable($max) { $a = 0; for ($i=0; $i<=$max; $i++) { $a+=$i; } return $a; } public static function addingPairs($max) { return ($max+1)*($max/2); } }
Result 1: 2450035000 Memory Peak 1:6460472 Result 2: 2450035000 Memory Peak 2:6460472 Result 3: 2450035000 Memory Peak 3:6460472
Die erste Funktion ist offensichtlich die speicherintensivste (durch die Nutzung von range()), und dadurch bekomme ich keine (bzw. falsche) Werte für die nachfolgenden Funktionen. Ändern der SumFunctions-Klasse ist übrigens verboten, dadurch fällt der Einsatz von memory_get_usage() weg. Manuelle Garbage-Collection mittels gc_collect_cycles() bringt in diesem Fall auch nichts da zwar der Speicher aufgeräumt wird, aber nicht der Peak-Wert zurückgesetzt wird
Die einzige Lösung die ich aktuell sehe (habe es noch nicht ausprobiert) wäre der Einsatz einer tick-Funktion um dann selbst den Peak mit memory_get_usage() zu ermitteln, so schleichte ich mich quasi in die aufgerufenen Funktionen ohne sie zu ändern, und kann meinen eigenen Peak-Wert zwischen den Funktionen resetten.
Gibt es für dieses Problem keine andere, leichtere, performantere, vernünftigere Lösung?
Wie wäre es mit XDEBUG und dem Profiler?
Arne
21 Jul 12 at 17:14
@Arne Geht leider nicht, XDebug steht nicht auf allen Maschinen zur Verfügung wo diese Messung laufen soll. Es können nur Basic-PHP-Funktionen genutzt werden.
Michael Kliewe
21 Jul 12 at 17:17
Wie wäre es dann mit XHProof? Ich denke nicht das du um einen Profiler herum kommst.
Ole
22 Jul 12 at 21:58
@Ole Gleiches Problem wie bei XDebug: Die Machinen sind nicht unter meiner Kontrolle, nur eine Minderheit „da draußen“, bei denen das Projekt laufen wird, hat eine dieser Extensions installiert.
Es geht nicht um eine einmalige Messung auf meinem Rechner, sondern es soll bei allen, die das Script laufen lassen, den Peak messen und bei Bedarf reagieren.
Michael Kliewe
22 Jul 12 at 23:42
Hmm wie wäre es dann damit die functionen einfach jeweils in einer clousure zu speichern und dann diese in einer zufälligen reihenfolge auszuführen. Wie das jetzt zum endgültiten ergebniss führen soll weiß ich allerdings grad auch nicht 😀
Ole
22 Jul 12 at 23:45
Ich spaar mir mal den üblichen ‚Bad Application Design‘ Krempel und stelle mal eine Vermutung auf.
Wenn du gc_disable() aufrufst, kannst du dann nicht den Verbrauch der einzelnen Funktionen selbst berechnen?
Jan Pietrzyk
23 Jul 12 at 23:00
@Jan Pietrzyk Wenn du mir deinen letzten Satz noch etwas näher erklären könntest, ich ahne in welche Richtung das gehen soll, aber wie soll das im Detail funktionieren? Ich seh da keine Lösung ohne die Funktion, die gemessen werden soll, mit memory_get_usage() zu spicken? Das Grundproblem ist ja leider dass die Funktionen, die gemessen werden sollen, nicht verändert werden können/sollen, man muß also den Verbrauch „von außen“ messen, oder sich evtl. mit der Tick-Funktionalität da doch reinschleichen, aber ohne den Code der zu untersuchenden Funktionen anzufassen…
Michael Kliewe
25 Jul 12 at 00:23
@Jan Pietrzyk so, nachdem ich drüber geschlafen hab könnte es funktionieren 😉 Man würde also am Anfang gc_disable() die Garbage-Collection komplett ausschalten, Funktion1 aufrufen, danach den Verbrauch mit memory_get_usage() messen und dann einmal die Garbage-Collection laufen lassen gc_collect_cycles() und hoffen dass der Verbrauch damit wieder auf den Basiswert fällt, und dann Funktion2 aufrufen… Könnte funktionieren wenn bei deaktivierter Garbage-Collection wirklich gar nichts an unnötigem Speicher gelöscht wird. Werd ich mal ausprobieren!
Michael Kliewe
25 Jul 12 at 08:49
‚gc_disable()‘ deaktiviert aber leider nicht die komplette Gargabe-Collection, sondern nur den Cyclic-Reference-Collector. Der „normale“ Garbage-Collector, der bei 0-referenzierten zvals einspringt, arbeitet weiter.
KingCrunch
25 Jul 12 at 22:54