PHPGangsta - Der praktische PHP Blog

PHP Blog von PHPGangsta


Maximaler Arbeitsspeicherverbrauch mehrerer Funktionen

with 9 comments

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?

Written by Michael Kliewe

Juli 21st, 2012 at 3:46 pm

9 Responses to 'Maximaler Arbeitsspeicherverbrauch mehrerer Funktionen'

Subscribe to comments with RSS or TrackBack to 'Maximaler Arbeitsspeicherverbrauch mehrerer Funktionen'.

  1. Wie wäre es mit XDEBUG und dem Profiler?

    Arne

    21 Jul 12 at 17:14

  2. @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

  3. Wie wäre es dann mit XHProof? Ich denke nicht das du um einen Profiler herum kommst.

    Ole

    22 Jul 12 at 21:58

  4. @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

  5. 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

  6. 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

  7. @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

  8. @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

  9. ‚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

Leave a Reply

You can add images to your comment by clicking here.