T-Rex: PHP bzw. Apache mehr Leistung geben

Moin,

wie aus anderen Fragen ersichtlich habe ich ein Projekt mit sehr vielen Berechnungen. Ich bin mit der Optimierung der Programmierung an meine Grenzen gestoßen. Deshalb würde ich gerne die Leistung von PHP bzw. dem Apache Webserver steigern.

Schaue ich mir die Leistung im Task-Manager an, so befindet sich die CPU zwischen 5% - 10% (insgesamt bei max. 15%) beim Apache. Da ich aktuell schon auf meinem Game Computer programmiere, die Berechnungen insgesamt aber nicht schneller laufen als auf meinem deutlich langsameren Notebook, denke ich, dass irgendwo eine Bremse eingebaut ist. Für normale Anwendungen wie hust Webseiten mag das auch sinnvoll sein. Ich würde aber gerne die Leistung hochschrauben. Die CPU kann auch gerne bis 90% benutzt werden. Der Computer kann gerne heiß laufen.

Ich benutzte xampp mit der Standardeinstellung. Wo bzw. wie kann ich das System mehr powern?

Gruß xertt

  1. Wenn die CPU trotz lang laufender Prozesse „idelt“ wird diese entweder nicht ausreichend mit Daten (von Festplatte oder Arbeitsspeicher) gefüttert(1) oder der Scheduler weist leer laufenden Prozessen Rechenzeit in großen Blöcken zu (2). Du programmierst Dein Zeug in einer Skriptsprache (3).

    (1) Flaschenhals suchen.

    (2) Naja. XAMPP. Nichts davon ist explizit für Windows programmiert, nur (teils mit Krücken-Libs) dafür kompiliert. Und den Apache gibt es nur von Drittanbietern als Windows-Version. Als man die noch bei Apache.org beziehen konnte hat man dort vor einen produktiven Einsatz unter Windows ausdrücklich wegen fraglicher Sicherheit und Leistungseinschränkungen gewarnt. Was solltest Du also tun?

    (2a) Du hast ein Desktop-Windows. Ich vermute (aus für mich nahe liegenden Gründen) dass der Scheduler eines Windows-Servers kleinere „Zeitscheiben“ vergibt.

    (3) Womöglich ist eine andere Programmiersprache für Dein konkretes Problem besser geeignet. Versuchs doch mal mit C++ oder Python.

    (4) Ob der Taskmanager wohl alles korrekt wieder gibt?

    1. Starte das PHP-Skript mal nicht via Browser und als Apache-Modul, sondern im Terminal - und zwar so, dass es die Ausgabe in eine Datei wegschreibt. Geht das schneller?

    2. Ich liebe Tipps wie die (3). Ja klar ... ich werfe die letzten Monate einfach weg, lerne eine neue Programmiersprache und mach alles neu. Wenn ich am selben Punkt stehe (also so in einem Jahr), wird wieder jemand sagen: "nee also sowas macht man aber mit einer ganz anderen Programmiersprache".

      Ich will auch keinen Flaschenhals suchen - zumal ich das schon immer mal wieder mache. Es geht hier auch nicht mehr um eine effektivere Programmierung. Ich dachte da an sowas naives wie den Arbeitsspeicher hochdrehen.

      memory_limit = 256M und fertig.

      Gibts sowas auch für die CPU? Hier sind 8 Kerne und ich hab das Gefühl, dass nicht mal ein halber benutzt wird.

      Vielleicht stelle ich mir das auch zu einfach vor...

      Gruß (PHP) T-Rex

      1. Lieber T-Rex,

        Ich will auch keinen Flaschenhals suchen

        warum nicht? Bist Du sicher, dass Du nirgendwo einen hast?

        Es geht hier auch nicht mehr um eine effektivere Programmierung.

        Und wenn jetzt Dein Problem aber genau daran hängt? Bist Du so gut im Programmieren, dass Du hier ganz sicher und mit Bestimmtheit sagen kannst, dass hier bereits alles an Konzepten und Vorgehensweisen ausgereizt ist? Vielleicht liegt ja zu unser aller Überraschung doch vielleicht genau dort ein Flaschenhals?

        Ich dachte da an sowas naives wie den Arbeitsspeicher hochdrehen.

        Hmm, mit naiv kommen wir da ganz sicher nicht sinnvoll weiter. Mehr Arbeitsspeicher hilft nur dann, wenn der offensichtlich zu knapp geworden ist (out of memory error).

        Gibts sowas auch für die CPU? Hier sind 8 Kerne und ich hab das Gefühl, dass nicht mal ein halber benutzt wird.

        Der Apache zieht sich genau die Leistung, die er benötigt. Probiere doch einmal das hier und beobachte im Task-Manager, was passiert:

        while (true) {
          file_put_contents(
            './endlos.txt',
            "- eine neue Zeile\r\n",
            FILE_APPEND
          );
        }
        

        Das sollte theoretisch auf einem Kern 100% Last erzeugen. Naja, heute tut es das vielleicht nicht mehr, mit PHP 4 hat es das aber damals getan. Weil das nicht auf mehrere Kerne aufgeteilt abgearbeitet werden kann, wird es nur einen Kern belasten. Mehrere zeitnahe Requests können durchaus auf unterschiedlichen Kernen abgearbeitet werden. Das kann man aber php-seitig nicht aus einem Script heraus beeinflussen, denn das regelt meines Wissens der Apache dynamisch.

        Vielleicht stelle ich mir das auch zu einfach vor...

        Dann ist das vielleicht ein Indiz dafür, dass Du uns vielleicht doch eine Kostprobe von Deinem PHP-Code geben solltest, damit man erkennen kann, warum das nicht so flutscht, wie Du das gerne hättest.

        Liebe Grüße

        Felix Riesterer

        1. Hallo Felix,

          Probiere doch einmal das hier und beobachte im Task-Manager, was passiert:
          (Schleife mit file_put_contents) Das sollte theoretisch auf einem Kern 100% Last erzeugen.

          Aber auch nur theoretisch. Praktisch kommt File-I/O und Betriebssystemoverhead hinzu, womit die 100% Last auf einem Kern zum Teufel sind.

          100% Last auf einem Kern bekommst Du zum Beispiel mit

          <?php
          $t = microtime(true);
          $a = 3;
          for ($j=0; $j<200; $j++) {
             for ($i=0; $i<1000000; $i++) {
                $a = 1 - $a;
             }
             echo ".";
          }
          echo "\nRuntime: " . (microtime(true) - $t)*1000 . "ms\n";
          

          Der ECHO alle 100000 Durchläufe hat den Sinn, dass man das Script mit Strg+C abbrechen kann. Andernfalls müsste man den PHP Prozess im Taskmanager töten.

          Dieses Script läuft bei mir unter PHP 8.1 mit aktiviertem JIT Compiler deutlich fixer als unter 8.0 (wo der JIT ebenfalls aktiv ist). Vielleicht hilft das schon. Der JIT ist Teil der opcache-Erweiterung, das ist eine zend-Extension, die in der php.ini per Default deaktiviert ist.

          Rolf

          --
          sumpsi - posui - obstruxi
          1. JIT Compiler. Hört sich toll an. Leider ist weder in der php.ini etwas von diesem Compiler zu finden noch funktionieren die Tutorials zum aktivieren :(.

            Gruß unkreativer T-Rex

            1. Hallo T-Rex,

              welche PHP Version verwendest Du?

              Rolf

              --
              sumpsi - posui - obstruxi
              1. PHP Version 8.1.1

                1. Hallo T-Rex,

                  Was meinst du damit, dass die Tutorials zum Aktivieren nicht funktionieren?

                  Du hast vermutlich nicht die Windows-Version von PHP im Einsatz, oder?

                  Bei der ist es simpel - einfach die entsprechende zend_extension in der php.ini hinzufügen, und opcache.jit_buffer_size setzen. Das ist per Default 0, dann ist der JIT inaktiv. Ich habe 4M gesetzt. Das kann ich aber nur lokal auf meinem Computer, nicht bei meinem Hoster.

                  Natürlich weiß ich nicht, ob dein PHP mit entsprechenden Schaltern compiliert ist. Das PHP bei meinem Hoster scheint es zu haben, ich komme da aber nicht an die php.ini heran, deswegen kann ich es da nicht aktivieren.

                  Wenn Du ein phpinfo-Script laufen lässt, sollte man die aktiven Werte sehen, opcache.jit sollte auf "tracing" stehen, und kurz vorher steht, ob der Jit disabled ist oder nicht

                  Also sowas:

                  <?php
                  phpinfo();
                  

                  Aber nicht auf dem Server lassen 😉

                  Wenn der Opcache.Jit aktiv ist, kann es schonmal auch helfen, die Dateien im Filecache zu löschen (siehe opcache.file_cache in php.ini). Ist mir passiert - ich hab den Buffer auf 0 gesetzt, Testlauf gemacht, langsam. Hochgesetzt, Testlauf langsam. Filecache geputzt - VROOOOM.

                  Es läuft aber am Ende wohl darauf hinaus, dass ein Kern für deinen Job zu langsam ist. Und wenn dein Algorithmus ausoptimiert ist, und PHP 8.1 schon am Start ist, tjaaaaa...

                  Rolf

                  --
                  sumpsi - posui - obstruxi
        2. Ein Nachtrag:

          der erste Lauf auf der Commandline unter PHP 8.1 braucht genauso lange wie der unter PHP 8.0. Auf beiden Versionen ist

          opcache.enable=1
          opcache.enable_cli=1
          opcache.jit=tracing
          opcache.jit_buffer_size=4M
          

          gesetzt. Der zweite Lauf ist unter PHP 8.1 aber viel schneller (650ms statt 3600ms). Die Scriptversion mit Zeitmessung habe ich im vorigen Beitrag hineineditiert (die mit der FOR Schleife)

          Rolf

          --
          sumpsi - posui - obstruxi
        3. Hello Felix,

          Der Apache zieht sich genau die Leistung, die er benötigt. Probiere doch einmal das hier und beobachte im Task-Manager, was passiert:

          while (true) {
            file_put_contents(
              './endlos.txt',
              "- eine neue Zeile\r\n",
              FILE_APPEND
            );
          }
          

          Diese Vorgehensweise ist mMn nicht zielführend, da die namensbasierten Dateifunktionen (Wrapper) alle "atomistisch" arbeiten, also bei jedem Durchlauf ein Handle anfordern, benutzen, schließen müssen. Das kostet Zeit.

          Besser wäre es, eine handlebasierte Funktion fopen() zu benutzen.

          Vielleicht magst Du mal die unterschiedlichen Geschwindigkeiten vergleichen?

          Selbst, wenn bei file_put_contents() das Handle gehalten wird bis zum Scriptende, muss es doch bei jedem Schleifendurchlauf erst wieder mit dem Namen verbunden werden. Das kostet dann zwar nicht genauso viel Kraft, wie ein neues anzufordern, aber bedeutet auch vermeidbaren Overhead.

          Verantwortlich dafür ist mWn der Statusccache. Wenn man z. B. zwischendurch die aktuelle Größe der Datei mit der namensbasierten Funktion filesize() feststellen will, muss man den vorher mit clearstatchache() löschen, während man mit fstat() direkt auf den Infoblock des geöffneten Handles zugreifen kann.

          Generell gilt auch in PHP immer noch die Regel:
          Je näher man sich an die Funktionen des OS/API hält, desto schneller wird es.

          Glück Auf
          Tom vom Berg

          --
          Es gibt nichts Gutes, außer man tut es!
          Das Leben selbst ist der Sinn.
      2. Wenn die CPU trotz lang laufender Prozesse „idelt“ wird diese entweder nicht ausreichend mit Daten (von Festplatte oder Arbeitsspeicher) gefüttert(1)

        (1) Flaschenhals suchen.

        Ich will auch keinen Flaschenhals suchen

        Mit allem Verlaub: Warum fragst Du dann? Wenn die CPU idelt bekommt die nicht ausreichend schnell Daten (oder wird sie nicht schnell genug los) oder wird durch das Scheduling gebremst.

        • https://www.computerwissen.de/windows/windows-tuning/wie-sie-einem-programm-maximale-leistung-zuweisen

        Und hiervon genau das Gegenteil machen:

        • https://www.getwox.com/de/set-cpu-priority-to-prefer-foreground-apps/

        Du hast bei Rolf gelesen, was der Umstieg auf PHP 8.1 in manchen Situationen bringt?

        Hier sind 8 Kerne

        Ich vermute nicht, dass bei Deiner Fallkonstellation mit Apache/PHP (vor allem nicht unter Windows) die Zahl der Kerne einen Vorteil bringt. Es geht Dir ja um einen einzelnen Programmablauf, nicht um parallele. Auch unter Linux sehe ich oft, dass nur ein Kern voll beschäftigt ist. Mit einem voll ausgelasteten Kern hast Du dann eben scheinbar nur 12,5% Auslastung des gesamten Prozessors.

        Viele Kerne bieten genau dann einen Vorteil wenn Programm und Programmiersprache das unterstützen oder wenn viele Prozesse (Threads) gleichzeitig die Leistung brauchen. Und bei PHP beginnt das hier:

        • https://learntutorials.net/de/php/topic/1583/multi-threading-erweiterung
        • https://www.php.net/manual/en/class.pool.php

        ... und das geht nicht, wenn PHP als Apache-Modul läuft.

        Zudem musst Du Dir dann Gedanken machen, wie Du dass mehr oder weniger linear ablaufende Programm auf mehrere Threads aufteilst. Das ist oft nicht ganz einfach, weil dann z.B. Prozess 0815 wissen muss, wann 0406 mit welchem Ergebnis fertig ist…

        Es geht hier auch nicht mehr um eine effektivere Programmierung.

        Ah! Sowas willst Du also:

        Besorg Dir Räucherstäbchen, eine Kröte (ein saurer Hering tut es auch). Werfe die Räucherstäbchen bei Vollmond in eine Wanne mit kaltem Wasser, zünde den Hering an und rufe 3 mal laut „Tohubawu”.

        1. Hallo Raketenwilli,

          wie gesagt, die pthreads-Erweiterung gilt laut php.net als unmaintained, darauf würde ich nichts aufsetzen.

          Über alternative Ideen hatte ich vorhin nachgedacht. Im Apache KANN es gehen, wenn er gut eingestellt ist, man muss dann nur Arbeits-Requests "an sich selbst" schicken. Und zwar asynchron. Der Apache sollte dann merken, dass einer seiner PHP Threads noch busy ist und den Request an einen anderen Thread schicken.

          Gerade sehe ich noch, dass php.net die parallel Extension empfiehlt.

          Und nun sei doch nicht so böse mit ihm 😉

          Ich hab heute was gelernt: Managing Assumptions. Da werden Annahmen, die man trifft, in 4 Kategorien eingeteilt

          • Known Knowns: Dinge, die man kennt und im Griff hat

            • Die geleisteten Codeoptimierungen
            • Die Erkenntnis, dass man mit Codeoptimierungen nicht weiter kommt
          • Known Unknowns: Dinge, von denen man weiß, dass man sie noch nicht im Griff hat

            • Kann man den Algorithmus an sich verbessern
          • Unknown Knowns: Dinge, man nicht kennt, die einem aber jemand anders sagen könnte. Wenn man nur wüsste, wer. Das Self-Forum ist dafür ein guter Ort

            • Wie kann ich meine Kiste konfigurieren, um mehr Leistung zu bekommen
          • Unknown Unknowns: Das alltägliche, unvorsehbare Chaos.

            • Die CPU brennt durch oder er bricht sich einen Finger.
            • Die Erkenntnis, dass man seine Annahmen falsch einsortiert hat

          Er kommt also zu uns auf der Suche nach einem "unknown known", und bekommt zur Antwort, dass seine "unknown known" in Wahrheit auf sein existierendes "known unknown" hinausläuft, nämlich Parallelisierung des Algorithmus, und ein weiteres known unknown hinzukommt: Mit einfachem PHP geht das nicht

          Das muss man nun erstmal verdauen. Ob es einen dann nährt, oder man sich darüber erbricht, ist dann die große Frage 😂

          Rolf

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

    wieviele Kerne hat deine CPU? Deine CPU hat 8 Kerne, schriebst Du.

    Nennst Du uns die Gesamtbelastung der CPU, oder die von einem Kern?

    Bei einer 8-Kern CPU wäre die maximale Auslastung eines Kerns, plus etwas OS-Overhead, gerade die 15% Gesamtlast. Und PHP ist single threaded, d.h. es kann die Arbeit nicht auf mehrere Kerne verteilen. Dafür brauchst Du eigene Arbeits-Threads, und das ist eine ganz andere Form der Programmierung. Es gibt die pthreads-Erweiterung in PHP, aber die gilt als tot. Der Nachfolger pht ist für PHP 7.2 - ausschließlich. Vielleicht findet man bei PECL noch andere.

    Ein anderer Bremsfaktor kann zu wenig Speicher sein, aber das wird auf deinem Gaming Notebook wohl nicht gelten. Du kannst das im Taskmanager prüfen - wie verhalten sich während der Rechnung die Seitenfehler für den PHP Prozess? Bzw. für Apache falls Du mod_php verwendest?

    Wenn deine Berechnungen zwischendurch Festplatten- oder Datenbankzugriffe brauchen, ist das ein weiterer limitierender Faktor. Du kannst dann versuchen, die Daten vorher en bloc in den Speicher zu lesen, das mag schneller sein als sie zwischendurch einzeln zu holen.

    Und natürlich gilt immer, dass die beste Optimierung nichts nützt, wenn der Algorithmus schlecht ist. Aber das können wir von hier aus nicht beurteilen.

    Für handgemachte Parallelverarbeitung: Hast Du genug Speicher? Wie lange laufen Deine Berechnungen? Wie gut lassen sie sich in unabhängige Großblöcke verteilen? Wenn sie lange brauchen und sich große, unabhängige Teilbereiche bilden lassen, könntest Du die Arbeitsdaten in eine Datei schreiben, einen separaten PHP Prozess starten der sie verarbeitet und das Ergebnis in eine andere Datei stellt, und das wieder einlesen. Wie gesagt: das lohnt nur bei VIEL Laufzeit, und Du brauchst genug RAM für die Zusatzprozesse. Statt Dateien kannst Du auch mit Shared Memory arbeiten, dafür brauchst Du aber ein speziell compiliertes PHP und es geht nur unter Linux. Es gibt auch das Konzept der "Named Pipes" um Daten von einem Prozess zum anderen zu bekommen, aber das hab ich noch nicht gemacht und müsste erst recherchieren.

    Eine weitere Alternative ist, mehrere PHP Prozesse (so viele, wie Du Kerne übrig hast) mit der -S Option zu starten, jeder auf einem eigenen Port, und die Teilberechnungen per HTTP Request an sie zu verteilen. Auf einem öffentlichen Webserver klappt das natürlich nicht so einfach…

    Für das Management von Workern kann auch der FastCGI Manager helfen, aber das ist, glaub ich, ein Monster für sich.

    Rolf

    --
    sumpsi - posui - obstruxi
    1. Hello,

      wieviele Kerne hat deine CPU? Deine CPU hat 8 Kerne, schriebst Du.

      Nennst Du uns die Gesamtbelastung der CPU, oder die von einem Kern?

      Bei einer 8-Kern CPU wäre die maximale Auslastung eines Kerns, plus etwas OS-Overhead, gerade die 15% Gesamtlast. Und PHP ist single threaded, d.h. es kann die Arbeit nicht auf mehrere Kerne verteilen.

      Mal unwissend zurückgefragt:
      Was passiert, wenn man in PHP unter Linux exec() benutzt und den Aufruf in den Hintergrund stellt? Ist der dann 100% Child vom Hauptprozess, oder bekommt der einen neuen Thread und damit eventuell auch anderen Prozessorkern?

      Glück Auf
      Tom vom Berg

      --
      Es gibt nichts Gutes, außer man tut es!
      Das Leben selbst ist der Sinn.
      1. Moin,

        Was passiert, wenn man in PHP unter Linux exec() benutzt und den Aufruf in den Hintergrund stellt? Ist der dann 100% Child vom Hauptprozess, oder bekommt der einen neuen Thread und damit eventuell auch anderen Prozessorkern?

        sowohl als auch.

        Mit exec() wird ein komplett neuer Prozess gestartet (nicht nur ein Thread), der seinen eigenen Eintrag im Task-Manager bekommt, vom Scheduler genauso bedient wird wie alle anderen, der sogar mit seinem Eltern-Prozess, dem PHP-Interpreter, um CPU-Zeit konkurriert. Und ja, er kann auf einem anderen Kern laufen, wenn der Scheduler meint, dass das gut wäre.

        Gleichzeitig ist er aber ein Kind-Prozess von PHP in dem Sinn, dass der Elternprozess alle Rechte daran hat. Er kann dem Kind-Prozess z.B. die Priorität erhöheh und verringern, er kann ihn auch vorzeitig abbrechen.

        Einen schönen Tag noch
         Martin

        --
        Мир для України.
        1. Hallo Martin,

          prinzipiell hast Du ja recht, aber exec hat den Nachteil, dass es auf das Ende des gerufenen Programms wartet und damit für asynchrone Worker nicht geeignet ist.

          Just learned: Einen asynchronen Prozess kriegst Du mit proc_open hin (bidirektional) oder popen (nur die Ausgaben lesen) hin. Da bekommst Du die Kommunikation mit Pipes fertig geliefert. Ob das in einem Webrequest zufriedenstellend funktioniert, ist allerdings die große Frage.

          Heißt also:

          (1) Aufgabe so planen, dass sie in max. 6 autarken Teilbereichen lösbar ist (bei 8 Kernen. Einer für den Webserver, einer für's OS, 6 für die Arbeit) (2) Je Teilbereich: Daten für die Aufgabe isolieren, Worker mit proc_open starten, Dateien in seine Eingangspipe jagen, Eingangspipe schließen (3) Ggf. noch einen Rest der Aufgabe selbst bearbeiten (4) Die Output-Streams der Worker round robin nach Antworten abfragen, Daten einlesen, ggf. zwischenspeichern, ggf. gleich integrieren, bis alle Worker fertig sind (5) Gesamtlösung präsentieren.

          Das ist NICHT trivial, nutzt aber alle Kerne des Prozessors.

          Eine generischere Lösung würde das nicht mit proc_open, sondern, wie erwähnt, mit HTTP Requests machen und die Verteilung auf Worker dem Apache überlassen. Die HTTP Lösung skaliert besser, weil man nämlich nach Bedarf Requeste an andere Maschinen schicken kann. Sofern sich die Aufgabe soweit fragmentieren lässt und die Request-Latenzen hinnehmbar sind, heißt das,

          Rolf

          --
          sumpsi - posui - obstruxi
          1. Hello,

            prinzipiell hast Du ja recht, aber exec hat den Nachteil, dass es auf das Ende des gerufenen Programms wartet und damit für asynchrone Worker nicht geeignet ist.

            [...] nicht, wenn man den Prozess in den Hintergrund verfrachtet:

            $pid = exec("/usr/local/bin/php bg-script.php > /dev/null & echo $!");
            

            siehe auch ältere Postings.

            Glück Auf
            Tom vom Berg

            --
            Es gibt nichts Gutes, außer man tut es!
            Das Leben selbst ist der Sinn.
            1. <?php
              ### File: vater2.php
              header('Content-Type:text/plain');
              
              $pid = exec("kind.sh 3 > /dev/null & echo $!");
              echo $pid . PHP_EOL;
              $pid = exec("kind.sh 4 > /dev/null & echo $!");
              echo $pid . PHP_EOL;
              
              exit;
              
              #!/bin/bash
              ### File: kind.sh
              ### Caution! This will never ending!
              
              echo -n "$$ $0 $*" > "/tmp/$1.pid";
              nr=$1;
              while [ 1 -eq 1 ]; do
                      echo -n $1 >> "/tmp/${nr}";
                      echo -n ": " >> "/tmp/${nr}";
                      date >> "/tmp/${nr}";
                      echo "" >> "/tmp/${nr}";
                      sleep 1;
              done
              
              

              sollte tun und tut auch wenn ich es wie beschrieben wie mit at mache.

              (Nach Aufruf im Browser)

              ps ax | grep kind.sh
              

              findet die beiden Prozesse.

              ls tmp[1-9]*
              

              findet aber nichts. Es müsste je eine Datei 3, 3.pid, 4 und 4.pid aufscheinen und natürlich einen Inhalt haben.

              Hm. Bei meiner Version (auch im Browser gestartet) läuft das Skript, die Dateien 1, 1.pid, 2, 2.pid werden angelegt und „gefüttert“. Ich suche später weiter, warum der Mix aus Deiner Version des „Vaterprozesses“ und meinem „Kindprozess“ nichts tut - obwohl er sollte.

              Kannst ja mal untersuchen, was Dein Kind-Prozess tut: Kann der in Dateien schreiben?

              1. Jungens! Akademisch mag das ja alles irgendwo interessant sein, aber mir fehlt da letztlich der Erkenntnisgewinn. Niemand wird doch ernsthaft so eine Applikation bauen wollen?

                1. TS schon … und nicht ganz grundlos versuchen sich die Macher von PHP ja auch daran, die Skripte auf mehrere Prozesse verteilen zu können.

                  Für Dich mag es ein „unknown unknown“ sein. Andere sehen das anders…

                  1. Hallo Raketenwilli,

                    versuchen sich die Macher von PHP ja auch daran, die Skripte auf mehrere Prozesse verteilen zu können.

                    Wobei mehrere Threads innerhalb vom Prozess, mit Threadkommunikation und shared memory, auch interessant sind.

                    Ich bin nur gespannt, ob T-Rex hier aufgegeben hat oder sich nochmal meldet.

                    Rolf

                    --
                    sumpsi - posui - obstruxi
                  2. TS schon … und nicht ganz grundlos versuchen sich die Macher von PHP ja auch daran, die Skripte auf mehrere Prozesse verteilen zu können.

                    TS wirft immer gerne grundsätzlichen Erwägungen in den Raum. Sein gutes Recht. Für mich kommt da eigentlich nie irgendwas greifbares bei raus. Wenig überraschend IMHO. Was die PHP-Macher ist auch deren Ding. Ach, egal. Ich gehe jetzt Bier trinken.

      2. Was passiert, wenn man in PHP unter Linux exec() benutzt und den Aufruf in den Hintergrund stellt?

        Nicht was Du willst. Mit installiertem at „geht was“. Das installiert und aktiviert den Dienst atd, der dann das übergebene Programm unabhängig im Hintergrund ausführt. man at hilft Dir weiter:

        Nur nachmachen, wenn man weiß, was man tut:

        Vater-Prozess:

        <?php
        ### File: /tmp/vater.php
        
        foreach ( [1,2] as $i ) {
            $cmd="echo /tmp/kind.sh '$i' | at now";
        	echo "Starte $cmd\n";
        	exec($cmd);
        	echo "...Prozess $i gestartet\n\n";
        }
        exit;
        

        Kind-Prozess:

        #!/bin/bash
        ### File: /tmp/kind.sh
        ### Caution! This will never ending!
        
        echo -n "$$ $0 $*" > "/tmp/$1.pid";
        nr=$1;
        while [ 1 -eq 1 ]; do
        	echo -n $1 >> "/tmp/${nr}";
        	echo -n ": " >> "/tmp/${nr}";
        	date >> "/tmp/${nr}";
        	echo "" >> "/tmp/${nr}";
        	sleep 1;
        done
        

        Starten:

        /tmp$ php vater.php
        Starte echo /tmp/kind.sh '1' | at now
        warning: commands will be executed using /bin/sh
        job 7 at Sat Mar 26 10:54:00 2022
        ...Prozess 1 gestartet
        
        Starte echo /tmp/kind.sh '2' | at now
        warning: commands will be executed using /bin/sh
        job 8 at Sat Mar 26 10:54:00 2022
        ...Prozess 2 gestartet
        
        /tmp$ _
        

        (Das PHP-Skript ist an dieser Stelle beendet. Entweder baust Du etwas, was dieses am Leben erhält oder Du startest (JS, xmlhttprequest,…) regelmäßig ein neues, welches z.B. auf die Existenz von Dateien reagiert und dann ggf. weiter macht. Damit verlierst Du dann aber den Vorteil, dass Du so auch die Speicher- oder Laufzeitbeschränkungen von PHP oder dem Apache umgehst... Bei Rolfs Lösung greifen diese weiterhin.)

        Dateien beobachten:

        /tmp$ tail -fn0 /tmp/1 /tmp/2;
        

        Prozesse beenden:

        /tmp$ killall kind.sh
        

        In den *.pid-Dateien seht jeweils wie folgt:

        10519 /tmp/kind.sh 1
        10524 /tmp/kind.sh 2
        

        ... also je eine Zeile mit "PID Befehl Argumente".

        Jetzt musst Du „nur noch“ schauen, wie Du das gesteuert bekommst und was passiert wenn der Apache das startet. Ich habe die Versuche als normaler Benutzer in einem Terminal gemacht. Mit einem anderen Benutzer und ohne Terminal kann das ganz anders ausgehen.

        1. Ich habe die Versuche als normaler Benutzer in einem Terminal gemacht. Mit einem anderen Benutzer und ohne Terminal kann das ganz anders ausgehen.

          Wenn eine Fehlermeldung wie 'You do not have permission to use at' im error-log (von PHP) auftaucht, dann muss in /etc/at.deny die Zeile www-data gelöscht werden.

    2. Das hört sich alles sehr gut an Rolf, vielen Dank.

      Generell wollte ich mich bei allen bedanken. Jede Idee bzw. jeder Lösungsansatz ist gut - entweder bringt er einen voran oder man kann Dinge ausschließen.

      Wenn ich via Javascript mehrere Aufrufe gleichzeitig mache, kann ich dann mehrere PHP Threads starten? Oder verhindert das der Apache, da der eventuell an nur einem Kern "klebt"? Wenn dem so ist, kann ich das irgendwo aufbrechen, dass andere Kerne benutzt werden? Wobei, ich erinnere mich dunkel das bei einer Antwort hier stand, dass der Apache bei ausgelastetem Kern automatisch einen freien nimmt?

      Es tut mir leid, dass ich so wenig Antworte. Gerade diese Woche müssen zwei Freunde umziehen :(. Ich bin gerade auch ein Multithread.

      Gruß kreativloser T-Rex

      1. Die Ausgabe von Linux-Befehlen wie ps -elF | grep apache2, top oder htop (alle ohne Root-Rechte) wird Dich davon überzeugen, dass der Apache von sich aus mehrere Threads bzw. Kindprozesse startet und diese die eigentliche Arbeit (und übrigens so (als Modul) oder so (als fcgi) auch PHP den PHP-Kram) machen lässt. Den Rest (Verteilung auf die CPUs) erledigt der Sheduler des OS.

        Deren Anzahl ist übrigens sogar konfigurierbar.

      2. Hallo T-Rex,

        Generell wollte ich mich bei allen bedanken. Jede Idee bzw. jeder Lösungsansatz ist gut - entweder bringt er einen voran oder man kann Dinge ausschließen.

        schön auf den Punkt gebracht.

        Wenn ich via Javascript mehrere Aufrufe gleichzeitig mache, kann ich dann mehrere PHP Threads starten?

        Ich weiß nicht, ob du hier gerade Client- und Serverseite zusammenwürfelst, aber ja, du kannst mit Javascript (vom Client aus) mehrere Anforderungen an den Server senden.

        Oder verhindert das der Apache, da der eventuell an nur einem Kern "klebt"?

        Der Apache ist so konstruiert, dass er für jeden Request einen separaten Thread aufmacht. Da PHP als Kind-Prozess vom Apachen gestartet wird, müssten sich so eigentlich auch mehrere PHP-Instanzen ergeben, die dann durchaus auf verschiedenen Kernen laufen können.

        Wobei, ich erinnere mich dunkel das bei einer Antwort hier stand, dass der Apache bei ausgelastetem Kern automatisch einen freien nimmt?

        Das kann der Indianer gar nicht bestimmen; das entscheidet das Betriebssystem (genauer: der Scheduler) für jeden Prozess. Das Programm selbst hat darauf keinen Einfluss.

        Es tut mir leid, dass ich so wenig Antworte. Gerade diese Woche müssen zwei Freunde umziehen :(.

        Na denn - 💪 wo ist das Klavier? 😉

        Einen schönen Tag noch
         Martin

        --
        Мир для України.
        1. Der Apache ist so konstruiert, dass er für jeden Request einen separaten Thread aufmacht.

          Das ist eigentlich im Hinblick auf die Frage „genug richtig“. Aber wenn man es genau nimmt startet der Apache als Vaterprozess mit Root-Rechten und setzt sofort eine konfigurierbare(¹) Mindestanzahl Kinder (ohne Root-Rechte) in die Welt, an welche er die eingehenden Request verteilt. Sind die Kinder alle beschäftigt, dann macht er - bis zu einem einstellbarer Maximum(¹) - weitere Kinder. Sitzen die aber lange faul rum, dann reduziert er deren Zahl wieder auf das Minimum oder bis alle ausreichend beschäftigt sind. Die Kindprozesse erledigen mehr als eine Anfrage. Aber auch nur eine konfigurierbare(¹) maximale Anzahl - um z.B. einer überbordendem Speicherreservierung vorzubeugen: Ist also (rein auf Grund der Statistik) damit zu rechnen, dass ein Kindprozess „krank von der Arbeit“ ist, wird er ersetzt.

          Man kann das auch wie einen Fahrradkurier beschreiben: Der Vaterprozess ist die Zentrale, die Kindprozesse die Kuriere und PHP wären dann die Pizzabäcker bzw. Köche.


          ¹) wie das genau geht hängt davon ab, ob mit welchem MPM-Modul man ihn betreibt.

          Na denn - 💪 wo ist das Klavier? 😉

          Klaviere sind gar nicht sooooo schwer. Einfach Tragegurte benutzen und die Dinger niemals alleine tragen - das beugt übermäßiger Anstrengung und Rückenschäden vor.

          1. Hallo Raketenwilli,

            das MPM Modul kann ich mir nicht aussuchen, oder? Da gibt's eins je Betriebssystem, wenn ich das richtig lese.

            Es gibt aber auch noch die Art, wie PHP angebunden ist: per mod_php oder als FastCGI (von klassischem CGI mit "starte für jeden Request einen neuen Prozess und röhre die Daten hinüber" rede ich mal besser nicht).

            Da man für mod_php die Threadsafe-Version von PHP verwenden muss, würde ich annehmen, dass mod_php einmaö PHP lädt und parallele Requests auf einen konfigurierbar großen Schwarm von Threads verteilt. Bei FastCGI würde ich in Analogie zu dem, was IIS treibt, davon ausgehen, dass mehrere PHP Prozesse gestartet werden und der Apache jeden einzelnen davon per Named Pipe oder lokalem IP-Socket versorgt.

            Wichtig ist aber auf jeden Fall, dass unabhängig von der Serverkonfiguration ein einzelner Webrequest immer singlethreaded verarbeitet wird, ganz gleich, wieviele Kerne die Maschine hat und wieviele parallele Threads ich dem Webserver gestatte. Für eine echte Parallelverarbeitung muss

            • dem Server das parallele Ausführen mehrerer Requests zu einem Binding gestattet sein
            • das PHP Script, das serverseitig läuft, mit Sessions aufpassen, denn Sessions sind Egoisten - es kann nur einen geben, der sie offen hat.
            • das PHP-Script, das den Request verarbeitet, Parallelthreads eröffnen (z.B. durch Abfeuern von Webrequests an seinen eigenen Host)
            • der Client, der den Request schickt, die Daten auf mehrere Requests aufteilen

            Die Idee mit dem Client-Treiber kommt von T-Rex und ist, wenn er ohnehin einen JS-Treiber hat, auch praktikabel. Die Asynchronität eines Webrequests ist in JS auf jeden Fall besser zu handhaben als in PHP: Daten auf N Eimer verteilen, N fetches abschicken. Wenn JS und Server auf der gleichen Kiste laufen, sollte man die fetch-Promises in ein Array packen und mit Promise.all warten, bis der Server fertig ist, um seine Kreise nicht allzusehr zu stören.

            Wenn der Server auf einer anderen Dose läuft, kann man überlegen, ob man die Antworten gleich bei Eintreffen so weit verarbeitet, dass sie für die Integration in die Gesamtlösung bereit sind. Das ist ein spannendes Thema 😀

            Rolf

            --
            sumpsi - posui - obstruxi
            1. https://httpd.apache.org/docs/2.4/mpm.html sagt:

              Der Server läßt sich besser auf die Bedürfnisse der jeweiligen Website anpassen. Sites beispielsweise, die eine hohe Skalierbarkeit benötigen, können ein Threaded-MPM wie worker oder event wählen, während Sites, die Stabilität oder Kompatibilität mit älterer Software erfordern, prefork wählen können.

              Also bei dem meisten Linuxen wird das prefork installiert. Das ist aber eher für Test- oder Entwicklersysteme (und wohl ein „muss“ wenn PHP als Apache-Modul betrieben werden soll) - also für geringe Lasten(¹). Hoch belastete Server und solche mit php als fcgi, suexec & co. laufen sehr gut mit dem Worker-Modul (und können, soweit ich das weiß, (wegen fcgi, suexec & co.) nicht mit dem prefork-Modul.)


              ¹) Bitte stellt Euch unter „geringe Lasten“ nicht zu wenig vor.

              1. Hallo Raketenwilli,

                ja okay, ich habe nur die Defaults gesehen.

                Ich finde die Apache Doku da etwas unübersichtlich. Konkrete Angaben, unter welchen OS welche MPM überhaupt möglich sind, finde ich schwer findbar. Es scheint, als würden prefork, worker und event nur für Unixoide vorgesehen sein, und mpm_winnt scheint explizit das Windows API zu nutzen, was einen Einsatz unter Unix/Linux ausschließt. Aber das muss man sich zusammensuchen. Hmpf…

                Zu den Unixoiden habe ich eben nur gelesen, dass heutzutage das mpm_event der häufigste Default wäre. Ob's dann auch immer das beste ich, kann ich nicht beurteilen. Aber für T-Rex dürfte das wurscht sein, solche Dinge entscheidet eh der Hoster.

                Rolf

                --
                sumpsi - posui - obstruxi
                1. Hallo Raketenwilli,

                  ja okay, ich habe nur die Defaults gesehen.

                  Ich finde die Apache Doku da etwas unübersichtlich. Konkrete Angaben, unter welchen OS welche MPM überhaupt möglich sind, finde ich schwer findbar. Es scheint, als würden prefork, worker und event nur für Unixoide vorgesehen sein, und mpm_winnt scheint explizit das Windows API zu nutzen, was einen Einsatz unter Unix/Linux ausschließt. Aber das muss man sich zusammensuchen. Hmpf…

                  Zu den Unixoiden habe ich eben nur gelesen, dass heutzutage das mpm_event der häufigste Default wäre.

                  Also Linux/BSD/Mac.

                  (Windows interessiert mich nicht, ich werde mir das nicht antun.)

                  Im auf Debian installierten Apache fand ich

                  • mpm_event
                  • mpm_prefork (das wird auch geladen)
                  • mpm_worker

                  Per default wird mpm-prefork aktiviert (ich kenne das nur so, weiß aber, dass explizite Webserver eher mit mpm-worker betrieben werden oder wurden). Man kann das selbst ändern, aber a2enmod schaltet den mpm eventuell um, wenn man ein Modul „Y“ installiert, welches ein MPM-Modul „X“ fordert, welches dann mit dem aktiviertem Modul „Z“ in Konflikt steht... (Das Programm „a2enmod“ (wohl aus apache-tools) ist geschwätzig und fragt bzw. zeigt das an.)

                  Die obigen mpm-module kommen wohl mit dem Paket apache2-bin.

                  Im Repo (Raspbian GNU/Linux 11 (bullseye)) wäre dann noch das nicht installierte Paket

  3. Also ich hab die Anforderung jetzt für mich gelöst.

    Da ich aus früheren Problemen heraus bereits auf Javascript gehen musste, da der Arbeitsspeicher für einen kompletten Durchlauf der Berechnung nicht ausreichte, war es relativ einfach die Anzahl der Anfragen nach oben zu schrauben. Ehrlich gesagt hatte ich das auch schon mal. Damals hatte ich aber 100 parallele Anfragen und habe keine bessere Leistung wahrgenommen (eventuell war das System mit so vielen Anfragen auch überfordert). Jetzt habe ich die Anfragen der Kerne angepasst. Da ich 8 Kerne habe, schicke ich 6 Requests weg (um den Computer nicht komplett zu belegen). Das scheint aktuell ungefähr 5-6 mal schneller zu sein. Das reicht mir im Moment, da Berechnungen die vorher 30 Minuten gedauert haben jetzt in ca. 7 Minuten abgearbeitet werden - super !

    Hinzu kommt, dass ich schlafende Windows Kerne aufgeweckt habe: https://www.youtube.com/watch?v=e7msQDa5jVc

    Einen JS Treiber habe ich nicht installiert. Die Scripte rufe ich via Browser auf, da ich auch eine Menge html Ausgaben brauche um die Ergebnisse zu sehen bzw. finale Ergebnisse weg zu speichern (um diese später erneut aufrufen zu können). Deshalb konnte ich über den Browser einfach das JS laufen lassen.

    Ich hab echt gedacht, dass irgendein Prozess innerhalb des Apache höhere Rechenleistungen auf mehrere Kerne verteilen könnte. Aber eure Erklärungen sind sehr sinnvoll. Danke, dass ich etwas lernen durfte.

    Gruß paralleler T-Rex T-Rex T-Rex T-Rex T-Rex T-Rex

    1. Hallo T-Rex,

      Hinzu kommt, dass ich schlafende Windows Kerne aufgeweckt habe: https://www.youtube.com/watch?v=e7msQDa5jVc

      Ich bin zwar nicht der absolute Prozessorspezialist - aber ich würde das, was da im Video passiert, ins Märchenreich verweisen.

      Wenn Du eine CPU mit Hyperthreading hast, dann kann der Prozessor konkurrierende Threads besser parallel ausführen, ja. Wenn Du 8 Threads hast, dann könnte es sein, dass ein Prozessor mit 4 Kernen und Hyperthreading diese in Summe schneller ausführt als ein Prozessor mit 4 Kernen OHNE Hyperthreading. Muss aber nicht sein.

      Sowas muss man nicht "aufwecken". Und schon gar nicht über die Leistungsüberwachung, denn das ist genau das, was der Name sagt: eine Überwachung des Ist-Zustandes, weiter nichts. Sie ändert nichts an der CPU-Konfiguration. Es könnte sein, dass die aktive Leistungsüberwachung mit der Idle-Messung des Taskmanagers kollidiert und der Taskmanager deshalb andere Auslastungswerte anzeigt.

      Dass ein normaler Desktopprozessor Kerne hat, die eigentlich nur bei Schäden einspringen und die man für den laufenden Betrieb hinzunehmen kann, halte ich für eine Erfindung. Es mag Serverprozessoren im High Availability Bereich geben, die sowas tun. Also die richtig teuren Xeons. Aber an diese Reservekerne kommst Du für den Normalbetrieb nicht heran. Weil sich Dein Prozessor sonst in einen Elefantenfuß verwandelt, bevor er durch's Motherboard tropft - Stichwort Thermal Design Power.

      Ob dein Prozessor Hyperthreading besitzt, erfährst Du am zuverlässigsten über die Typenbezeichnung und beispielsweise diese Liste.

      Er muss es aber nicht nur haben, es muss auch noch eingeschaltet sein. Die Wahrheit findest Du (Windows 10) im Taskmanager, "Leistung" Tab, Abteilung "CPU". Unter den Auslastungskurven siehst Du sowas:

      Mein Computer hat kein Hyperthreading, weil Kerne und logische Prozessoren gleich sind. Die meisten i5-er haben keins, die i7-er schon. Man kann es im BIOS ein- und ausschalten.

      Warum? Weil es nicht immer sinnvoll ist. Je nach Workload kann Hyperthreading das Gerät auch bremsen. Das hat Gründe:

      • mehr CPU Aktivität bedeutet mehr Wärme. Wenn deine Kühlung nicht ausreicht, taktet der Prozessor ggf. herunter, wenn Du ihn mit Hyperthreading quälst
      • Hyperthreading bedeutet, dass ein Kern 2 Threads bearbeitet. Der Trick ist dabei, dass ein CPU Kern aus sehr vielen Baugruppen besteht, die nicht immer alle gleichzeitig arbeiten. Hyperthreading bedeutet, dass die "einfacheren" Teile des Kerns doppelt existieren und damit die komplexeren Teile des Kerns besser ausgelastet werden können. Vor vielen Jahren haben die Kollegen bei c't das mal gemessen. Die Leistungszuwachs lag längst nicht bei 200% - das wäre ja auch merkwürdig. Es sind eher 20% bis 30%.
      • Wenn die Threads sehr speicherintensiv sind, überlastest Du mit zu vielen Threads ggf. den Prozessorcache, d.h. eine Verdopplung der Threads führt zu so vielen Cache Misses, dass Du unter dem Strich langsamer bist.
      • je nach Betriebssystem kommt der Thread-Scheduler mit zu vielen Kernen nicht gut klar. Das war bei Windows vor Win7 der Fall, ist heute also irrelevant.

      Gerade wenn viele PROZESSE parallel aktiv sind, bedeutet Hyperthreading richtig Stress für die Kiste. Denn jeder Prozess hat sein eigenes Speichermanagement (die Seitentabellen) und ein Kern, der zwei Prozesse hyperthreaden will, muss dafür ständig seine Speicherschaltkreise umschalten. Das ist teuer.

      Und dass ein zu hohe Zahl an Threads generell kontraproduktiv ist, hast Du ja auch schon gemerkt. Der Prozessor beschäftigt sich dann mehr mit Kontextwechseln als eigentlicher Arbeit.

      Rolf

      --
      sumpsi - posui - obstruxi