T-Rex: Mehrere Fragen bezüglich mehrere Tests großer Datenmengen

Moin,

ich hab da eine Datenmenge von 8000 Datensätze. Die lade ich einmal aus der Datenbank und unterziehe diesen verschiedene Tests. Präziser sind das diverse Berechnungen bei unterschiedlichen Gegebenheiten.

Dafür habe ich ein Array. Da stehen verschiedene Werte drin z.B. "höhe" von 1-10 oder "breite" von 1-10. Dann werden Tests und Berechnungen für alle Möglichkeiten durchgeführt. Es wird also geschaut was passiert bei höhe=1, breite=1 - was passiert bei höhe=1, breite=2 und so weiter. Bei dem hier gezeigten Beispiel sind es also 100 Tests. Vor einiger Zeit hat mein Computer ca. 9000 dieser Tests geschafft. Mittlerweile hat er bei 1000 Probleme (ich hab auch die Tests auch ordentlich aufgerüstet, sprich es wird mehr getestet). Die Ausführungszeit ist dabei gar nicht das Problem sondern der Speicherverbrauch. Bei 500MB ist Schluss.

Erstmal worum es mir NICHT geht. Ich möchte nicht die memory limit erhöhen! Es geht hier auch nicht um die Lesegeschwindigkeit der Daten aus der Datenbank. Dass passiert wie gesagt einmal und die Geschwindigkeit ist okay. Es geht auch nicht darum große Datenmengen zu handeln. Die Datenmenge ist mit 8000 Datensätze (später vielleicht bis 20.000 Datensätze) relativ klein. Also ich meine, es geht hier nicht um die Auswertung eines Datawarehouse.

Ich würde gerne wissen, wie man so viele Tests am besten handelt? Ich würde gerne noch mehr Tests machen. Eventuell auch 100.000 oder 1.000.000 gleichzeitig. Die Ausführungszeit ist dafür fast egal ... kann auch gerne über Nacht laufen. Ich habe mich mit so einem Thema nie beschäftigt und würde mich auch über jegliche Links freuen. Dennoch würde ich gerne Flaschenhälse in der Programmierung finden. Bei den Zeitlichen Komponenten schaffe ich das selbst, aber wie finde ich die stellen wo Speicher verschwendet wird, weil z.B. eine Copy eines Arrays oder eines Objektes erstellt wird und stehen bleibt?

Ich habe mir zudem überlegt jeden Test via Javascript an zu fordern. Dann müssten die Daten aber jedes Mal aus der Datenbank gelesen werden was einen sehr großen Overhead erzeugen würde. Da das System local läuft wäre die Übermittlungszeit wiederum egal. Ist das generell eine gute Idee?

Bislang habe ich eigentlich immer nur die Daten im klassischen Sinne behandelt - Webseiten Anfrage, Datenladen, bearbeiten, anzeigen.

Gruß Tausend Tester T-Rex

  1. Hallo T-Rex,

    ich hab da eine Datenmenge von 8000 Datensätze. Die lade ich einmal aus der Datenbank und unterziehe diesen verschiedene Tests. Präziser sind das diverse Berechnungen bei unterschiedlichen Gegebenheiten.

    du verrätst uns im Augenblick nichts darüber, in welcher Umgebung, unter welchen Bedingungen deine Tests laufen. PHP unter einem Webserver, das durch einen Browser-Request angestoßen wird?

    Erstmal worum es mir NICHT geht. Ich möchte nicht die memory limit erhöhen! Es geht hier auch nicht um die Lesegeschwindigkeit der Daten aus der Datenbank. Dass passiert wie gesagt einmal und die Geschwindigkeit ist okay. Es geht auch nicht darum große Datenmengen zu handeln. Die Datenmenge ist mit 8000 Datensätze (später vielleicht bis 20.000 Datensätze) relativ klein. Also ich meine, es geht hier nicht um die Auswertung eines Datawarehouse.

    Aber es hört sich so an, als wäre ein herkömmliches PHP-Script hier wegen des Rechenzeit- und Speicherbedarfs nicht mehr das richtige Werkzeug. Wenn du bei PHP bleiben willst - okay, könnte funktionieren. Aber dann würde ich eher ein vom Webserver unabhängiges Script vorschlagen, das beispielsweise über einen cron-Job angestoßen wird. Das darf dann auch gern üppig Soeicher brauchen und eine halbe Stunde oder so laufen.

    Vielleicht möchtest du für derart speicherintensive Tätigkeiten auch auf andere Lösungen ausweichen. Python wird gerade beim Handling großer Datenmengen hoch gelobt; C gibt dir größtmögliche Kontrolle über den Ressourcenverbrauch. YMMV.

    Immer eine Handbreit Wasser unterm Kiel
     Martin

    --
    Wenn ich den See seh, brauch ich kein Meer mehr.
    1. Moin Martin,

      eine andere Programmiersprache kommt nicht in Frage. Die Daten und die Datenübersicht ist in PHP. Außerdem würde es mich viel viel viel zu viel Zeit Kosten für das Thema eine neue Programmiersprache zu lernen mit deren Eigenheiten.

      Es geht nicht darum eine Lösung zu finden, die in 20 Jahren noch läuft. Auch bekommt wahrscheinlich nie jemand mein Programmierzeugs zu Gesicht. Ich möchte einfach nur Berechnungen anstellen und das Ergebnis haben.

      Ja PHP und ja, mittels Browserrequest. Da alles lokal läuft kann ich das PHP und den Webserver aufsetzen wie ich will. Aber wie gesagt, Speicher hochdrehen etc... soll hier nicht das Thema sein. Neben meinem Notebook hätte ich noch meinen Gamingpc, der das Thema Hardware komplett neu bewertet.

      Gibt es eine Möglichkeit zu sehen welche Variablen / Arrays / Objekte in einem Objekt wie viel Speicherplatz verbrauchen?

      Gruß PHP Depp T-Rex

      1. Hallo T-Rex,

        Gibt es eine Möglichkeit zu sehen welche Variablen / Arrays / Objekte in einem Objekt wie viel Speicherplatz verbrauchen?

        Jein.

        Problem 1: Es gibt keine Funktion "get_used_memory($a)" oder so. Es gibt memory_get_usage, aber das ist der insgesamt belegte Speicher von PHP.

        Problem 2: Eine solche Funktion kann irreführend sein. Gerade bei Arrays und Objekten können Querbeziehungen existieren - ein Objekt könnte in mehreren Arrays stehen.

        $arr1 = [ "Hallo", "Welt" ];
        $arr2 = [ "Self", "HTML" ];
        
        $arr3 = [ $arr1, $arr2 ];
        $arr4 = [ $arr1, $arr2 ];
        

        Es gibt den „Tipp“, strlen(serialize($a)) zu verwenden, um einen Daumenwert für den Speicherbedarf von $a zu erhalten. Mein Tipp wäre das nicht - es hat nur grob was mit dem real belegten Speicher zu tun und ist vor allem eine teuere und speicheraufwändige Operation. Vor allem: wenn Du das stumpf für $arr1 bis $arr4 machst und zusammenzählst, kommst Du auf einen viel zu hohen Wert. De facto wird der Speicher im Wesentlichen von $arr1 und $arr2 belegt. $arr3 und $arr4 enthalten nur Verweise auf dem Speicher von $arr1 und $arr2.

        Rolf

        --
        sumpsi - posui - obstruxi
  2. Hallo T-Rex,

    ob Du ein Speicherleck hast oder nicht kann man ohne Codeanalyse kaum sagen. Da hilft ggf. eine stringentere Strukturierung deiner Daten, vernünftige Nutzung lokaler Daten, so dass Du nach Abschluss einer einzelnen Berechnung sicher sein kannst, dass alle Arbeitdaten abgeräumt werden.

    Wenn Du tatsächlich hunderte von MB aktiv für die Berechnung brauchst, ist PHP möglicherweise die falsche Sprache. Oder Du musst eine 64-bit Version von PHP verwenden.

    Ich verstehe das so, dass Du N Parameter. Jeder Parameter kann eine gewisse Zahl von Werten annehmen. Diese Zahlen multipliziert man miteinander und bekommt eine bestimmte Zahl von Szenarios. Bei 2 Parametern zu je 10 Werten sind es 100 Szenarios. Und du möchtest jedes Szenario s auf jeden Datensatz d anwenden. Du hast also eine Menge von Berechnungen $$R(s_i, d_j)$$, mit $$s_i$$ als eins deiner Szenarios und $$d_j$$ als einen deiner Datensätze.

    Eine zu klärende Frage wäre nun, ob Du für die Berechnung von $$R(s_i, d_j)$$ auf ein $$R(s_p, d_q)$$ zurückgreifen musst, wobei p≠i oder q≠j sein sollte. Soll heißen: Eine Rechnung, die Du für ein anderes Szenario des gleichen Datensatzes angestellst, oder für das gleiche Szenario bei einem anderen Datensatz. Wenn das so ist, musst Du Dir überlegen, wie diese Abhängigkeiten sind und eine Strategie planen, wie Du möglichst wenige dieser abhängigen Rechnungen im Speicher halten musst.

    Wenn es keine solche Abhängigkeit gibt und jede Rechnung isoliert für sich ausgeführt werden kann, musst Du deine Speichernutzung analysieren. Wo legst Du Daten ab, wie kannst Du sicherstellen, dass nach Abschluss einer Rechnung alle Arbeitsdaten wieder verschwinden, und dann: was machst Du mit dem gefundenen Ergebnis? Muss es für irgendwas im Speicher bleiben? Wenn nicht: raus damit, entweder in eine Datei oder in eine SQL Tabelle. In einer SQL Tabelle hättest Du den Vorteil, dass parallele Zugriffe machbar sind, d.h. Du könntest mehrere Rechenbots parallel laufen lassen und nachher stehen in der Tabelle alle Ergebnisse. Wenn Du das mit einer Datei machst, musst Du sicher ein, dass das Locking funktioniert. Oder jeder Bot schreibt in eine eigene Datei, und Du integrierst die Dateien am Ende.

    Wenn Du tatsächlich ein memory leak hast und es nicht beseitigt bekommst (es könnte ja auch ein Bug in einer Library sein), hilft nur das Setzen von Checkpoints und ein gelegentlicher Restart. D.h. Du merkst Dir, bei welchem Szenario und welchem Datensatz Du warst, beendest das PHP Programm und sorgst durch ein Steuerscript dafür, dass es gleich wieder aufgerufen wird. Anhand des Checkpoints kann das PHP Programm dann an der richtigen Stelle wieder aufsetzen.

    Das ist jetzt alles abstrakt und ohne Kenntnis dessen, was Du eigentlich tust, nicht konkreter abzuhandeln.

    Rolf

    --
    sumpsi - posui - obstruxi
  3. Tach!

    Dann werden Tests und Berechnungen für alle Möglichkeiten durchgeführt. [...] Ich würde gerne wissen, wie man so viele Tests am besten handelt? Ich würde gerne noch mehr Tests machen. Eventuell auch 100.000 oder 1.000.000 gleichzeitig. Die Ausführungszeit ist dafür fast egal ... kann auch gerne über Nacht laufen.

    Dann muss es ja auch nicht vom Browser aus angestoßen werden. PHP hat seine Limits von Zeit und Speicher nur wenn es im Webserver eingebunden ist. Von der Kommandozeile aus gestartet kann es so viel Speicher und Zeit nehmen, wie das System zur Verfügung stellt.

    Wenn du dennoch den Browser zur Steuerung verwenden möchtest, wäre eine Aufgabenwarteschlange eine Möglichkeit. Der von der Kommandozeile oder zum Systemstart gestartete PHP-Prozess schaut gelegentlich nach, ob Aufträge drinstehen, arbeitet sie dann ab und schreibt das Ergebnis in den als erledigt gekennzeichneten Job. Vom Browser aus kann man die Einträge der Warteschlange steuern und überwachen.

    Beachte aber, dass PHP nicht dafür erfunden wurde, dauerhaft zu laufen. Es empfiehlt sich, auch noch einen Mechanismus laufen zu haben, der den PHP-Prozess überwacht und gegebenenfalls neu startet. Wie der aussehen könnte, kann ich dir nicht sagen. Vielleicht ein Cronjob. Andererseits könnte auch der Prozess zur Abarbeitung der Aufgabenwarteschlange cronjobgesteuert laufen und sich bei Nichtvorhandensein von Aufgaben wieder beenden. Damit muss nicht dauernd eine PHP-Instanz laufen, und der Cron-Mechanismus ist ausreichend stabil und verlässlich.

    dedlfix.

    1. Hallo dedlfix,

      vergiss es, diese Diskussion mit T-Rex hatten wir schon. Ein Kommandozeilen-PHP ist nichts für ihn. Das muss auf dem Server und im Browser gehen!

      Äh ja. Geht natürlich auch, aber mit den Laufzeit- und Speicherrestriktionen eines Webservers.

      Man könnte das von JavaScript aus steuern, indem man die Szenarien in JavaScript durchvariiert und für jede Kombi aus Szenario und Datensatz-ID einen Webrequest startet. Auf diese Weise kann man auch mehrere Requests parallel laufen lassen (bis zur Schmerzgrenze des Servers), sollte das aber im JS so steuern, dass die Anzahl der offenen Requests limitiert ist. Diese Nebenläufigkeit zu steuern ist nicht ganz trivial. Man könnte Webworker verwenden.

      Problem ist dann immer noch, die Ergebnisse irgendwo zu integrieren.

      Oder man rechnet im JS und verwendet einen PHP Service, um Ergebnisse am Server abzulegen.

      Rolf

      --
      sumpsi - posui - obstruxi
      1. Tach!

        vergiss es, diese Diskussion mit T-Rex hatten wir schon. Ein Kommandozeilen-PHP ist nichts für ihn. Das muss auf dem Server und im Browser gehen!

        Wenn es unbedingt im Webserver laufen muss, dann muss man schon sehr leidensfähig sein. Ein PHP-Script per Cron laufen zu lassen würde die Dinge so viel einfacher und zuverlässig machen. Allein schon, dass man nicht messen kann, wann zugeteilter Speicher und Zeit zur Neige geht, macht es so kompliziert einen lange laufenden Prozess am Laufen zu halten. Dass man dann auch noch eine ständige Netzverbindung zu einem den Prozess steuernden Browser braucht, macht es nicht einfacher. Wenn es denn so sein soll, bin ich raus, denn ich bekomme meine grauen Haare lieber auf natürlichem Wege als mit solchen Unwägbarkeiten.

        dedlfix.

        1. Hallo dedlfix,

          ich bekomme meine grauen Haare lieber auf natürlichem Wege als mit solchen Unwägbarkeiten.

          😂 immerhin kriegst Du welche. Meine fallen aus, bevor sie grau werden.

          Nein, ich habe den Eindruck, dass T-Rex einfach nicht die Möglichkeit zu einem PHP-Ablauf ohne Webserver hat. Wenn ich mich richtig erinnere, unterliegt er einem Unternehemsumfeld, das seine Arbeit nicht wirklich unterstützt. Wobei ich mir die Frage verkneife, ob er ein Fachbereichsnebenbeiprogrammierer ist (oder anders gesagt: der Horror der IT-Abteilung) oder ein dafür eingestellter Softwareentwickler innerhalb der IT.

          Rolf

          --
          sumpsi - posui - obstruxi
  4. Hallo,

    ich würde denken, dass PHP für eine solche Aufgabe die falsche Programmiersprache ist. Außerdem ist eine dynamische Datenstruktur, wie sie PHP für seine "Arrays" benutzt, Speicher- und Geschwindigkeitsfresser gleichermaßen.

    Dafür wird vermutlich ein klassisches statisches Array die bessere Wahl sein. Für eine genauere Beurteilung müsste man aber etwas über die benötigten Datentypen wissen.

    Nimm also z. B. C oder Pascal oder ähnliche machinennahe Hochsprachen für derartige Massenberechnungen.

    Erzähle uns doch auch noch etwas mehr über die Art der Berechnungen. Ist die datensatzübergreifend, sodass Du die Zeilen alle gleichzeitig laden musst?

    LG
    Ralf

    1. Hallo,

      ich würde denken, dass PHP für eine solche Aufgabe die falsche Programmiersprache ist. Außerdem ist eine dynamische Datenstruktur, wie sie PHP für seine "Arrays" benutzt, Speicher- und Geschwindigkeitsfresser gleichermaßen.

      Dafür wird vermutlich ein klassisches statisches Array die bessere Wahl sein. Für eine genauere Beurteilung müsste man aber etwas über die benötigten Datentypen wissen.

      Nimm also z. B. C oder Pascal oder ähnliche machinennahe Hochsprachen für derartige Massenberechnungen.

      Vielleicht kannst Du es die Datenbank auch selber rechnen lassen?

      Erzähle uns doch auch noch etwas mehr über die Art der Berechnungen. Ist die datensatzübergreifend, sodass Du die Zeilen alle gleichzeitig laden musst?

      LG
      Ralf

  5. Ich beginne an meinem Verstand zu zweifeln.

    Aber erstmal ... die Spekulation über meinen Job ist genau so Zielführend wie die Diskussion ob ihr eure Frauen befriedigen könnt. Deshalb schlage ich vor, die Diskussion wieder auf das Thema zu lenken auch wenn beide Themengebiete äußert interessant wären ;).

    Klar könnte man alles so umbauen, dass es in einer anderen Programmiersprache oder in der Console läuft. Zum einen fehlt mir dazu die Lust zum anderen die Zeit. Leider muss ich sagen, dass ein anfängliches Projekt ausgeufert ist. Dass kennt ihr bestimmt ... ein paar Funktionen (soll ja schnell sein,d a braucht es kein OOP) hier und da ein paar echos. Dann stellt man fest das reicht nicht, also baut man die Funktionen in Objekte um und so weiter ...

    Ich habe mich seit gestern sehr intensiv mit der Speicherthematik auseinander gesetzt und Verzweifel da wirklich...

    Zu erst habe ich meine Objekte so umgebaut, dass die rechen und speicherintensive Objekte nicht mehr für die Ausgabe zuständig sind. Das war auch ein klarer Design-Fehler. So konnte ich die Speicherlast pro Berechnung von 13MB auf 6MB runter bekommen. Jedoch erscheinen mir 6MB immer noch als sehr viel. Als ich dann im Produktumfeld nicht weiter gekommen bin habe ich eine kleine Testseite aufgesetzt und etwas rumgespielt. Vor allem beschäftigt mich die Frage wie ich den Speicher richtig frei gebe. Wie ich leider lesen musste reicht ein unset wohl nicht aus.

    Ich habe folgenden Code:

    $arArray = [];
    for( $iLoop = 0; $iLoop <= 1000000; $iLoop++)
    {	
    	$arArray[ $iLoop ] = md5( $iLoop );
    	$arArray[ $iLoop ] = null;
    	unset( $arArray[ $iLoop ] );
    }
    
    

    Vor und hinter dem Code speichere ich bzw. frage ich den Speicher ab. Das obige Beispiel bringt eine Speicherbelegung von 0,0008 MB.

    for( $iLoop = 0; $iLoop <= 1000000; $iLoop++)
    {
    	$arArray[] = md5( $iLoop );
    	$arArray[ count( $arArray ) - 1 ] = null;
    	unset( $arArray[ count( $arArray ) - 1 ] );
    }
    

    Dieses Beispiel braucht ca. 35 MB Speicherplatz. Untersucht man das Array genauer, so stellt man fest, dass die Array Keys bestehen bleiben. Meiner Meinung nach ist es aber die gleiche Methodik.

    Auf jeden Fall bringt mich das zu der Erkenntnis, dass ich keine Ahnung habe, wann und wie PHP den Speicher freigibt. Und wenn ich das im kleinen Testscript schon nicht verstehe, wie sollte ich es dann bei den komplexen Berechnungen verstehen?

    Noch ein Beispiel gefällig?

    for( $iLoop = 0; $iLoop <= 100000; $iLoop++)
    {
    	$arArray[ $iLoop ] = md5( $iLoop );
    }
    
    

    Braucht ca. 13 MB

    class cTestObjekt
    {
    	private $strMd5 = "";
    	public function setMd5( $strMd5 )
    	{
    		$this->strMd5 = $strMd5;
    	}
    }
    for( $iLoop = 0; $iLoop <= 100000; $iLoop++)
    {
    	$arArray[ $iLoop ] = md5( $iLoop );
    }
    
    

    Braucht ca. 6MB

    Wieso das Speichern in einem Objekt weniger Speicherplatz benötigt ist mir ein Rätsel. Ich weiß zwar nicht wie genau ein Objekt in PHP intern erzeugt oder gespeichert wird, hätte aber Overhead Daten vermutet, die den kompletten Speicherbedarf erhöhen.

    So oder so ... wie gesagt konnte ich einiges an Speicherplatzt einsparen, aber noch nicht genug. Deshalb werde ich wohl zu einem Workaround greifen und Javascript einsetzen.

    Gruß Speicherverschwender T-Rex

    1. Hallo T-Rex,

      beim [] bist Du auf eine böse PHP Falle gelaufen. Das Handbuch schreibt:

      Note that the maximum integer key used for this need not currently exist in the array. It need only have existed in the array at some time since the last time the array was re-indexed.

      D.h. die erste Zuweisung an $arr[] erzeugt Index 0, und danach ist der maximum integer key für dieses Array 0. Die zweite Zuweisung sieht 0, addiert 1 drauf ''und weist an Index 1 zu. Danach ist der maximum integer key 1, und das dritte $arr[] weist an Index 2 zu. Total egal, ob die Indexe darunter wieder entfernt wurden oder nicht. Es ist viel zu aufwändig, nach jedem Unset den maximum integer key neu zu bestimmen, dafür müsste PHP einmal durch alle Keys laufen.

      Wieso das Speichern in einem Objekt weniger Speicherplatz benötigt ist mir ein Rätsel.

      Mir auch. Vor allem, weil Du diese Klasse überhaupt nicht verwendest 😉. Wenn ich das nachbaue, neue cTestObjekte ins Array lege und für jedes setMd5(md5($iLoop)) aufrufe, braucht die Klassenversion ca 63% mehr Speicher.

      Rolf

      --
      sumpsi - posui - obstruxi
      1. Mir auch. Vor allem, weil Du diese Klasse überhaupt nicht verwendest 😉. Wenn ich das nachbaue, neue cTestObjekte ins Array lege und für jedes setMd5(md5($iLoop)) aufrufe, braucht die Klassenversion ca 63% mehr Speicher.

        Hmpf ... doch doch hab das schon verwendet. Es ist sehr mühselig die Testscripte hier zu posten, da ich nicht alles 1:1 übernehmen kann. Ich will ja auch niemanden erschlagen.

        Gruß Forum-User-Freundlicher T-Rex