Gunther: + (PHP) Cache vs. Datei einlesen

Hallo Selfgemeinde!

Ich hätte da mal eine Frage ...!
Und zwar würde mich mal interessieren, was in der Praxis eigentlich schneller, bzw. effizienter ist?

Annahme:
Ich habe eine Datei index.php, die bei jedem Request aufgerufen wird. Nun sollen in dieser Datei u.a. globale Konfigurationseinstellungen verfügbar sein, die in einer anderen Datei namens config.ini stehen.

Im Normalfall würde ich jetzt die entsprechenden Werte per parse_ini_file() jedesmal aus der config.ini einlesen.

Frage:
Da sich im Normalfall an den Daten in der config.ini nicht allzuhäufig allzuviel ändert, gibt es da eigentlich eine performantere Lösung?

Da es sich bei den (eingelesenen) Daten ja dann um ein mehrdimensionales Array handelt, könnte/ müsste ich es serialisieren und dann ebenfalls als Datei speichern. Und umgekehrt ebenfalls erst wieder den Dateiinhalt einlesen und anschließend deserialisieren.

Ich sehe da vom Grundsatz her eigentlich keinen Unterschied zwischen den beiden Varianten. Oder ist eine der beschriebenen Methoden signifikant schneller als die andere?

Oder gibt es generell eine performantere Methode so etwas umzusetzen?

Vielen Dank im Voraus für eure freundliche Unterstützung, bzw. Aufklärung.

Gruß Gunther

  1. hi,

    Frage:
    Da sich im Normalfall an den Daten in der config.ini nicht allzuhäufig allzuviel ändert, gibt es da eigentlich eine performantere Lösung?

    Für den Besucher, Cachen, ja. Nimm einfach die mtime der config.ini und setz die als Last-Modified in den Header zur index.php.

    Hotti

    1. Hi,

      Für den Besucher, Cachen, ja. Nimm einfach die mtime der config.ini und setz die als Last-Modified in den Header zur index.php.

      und wie 'cachen'? Schließlich brauche ich die Werte ja jedesmal in meiner index.php.
      Und wo ist da dann der (Performance-)Unterschied zu jedesmal per parse_ini_file() einlesen?

      Gruß Gunther

      1. Hi,

        Für den Besucher, Cachen, ja. Nimm einfach die mtime der config.ini und setz die als Last-Modified in den Header zur index.php.
        und wie 'cachen'? Schließlich brauche ich die Werte ja jedesmal in meiner index.php.
        Und wo ist da dann der (Performance-)Unterschied zu jedesmal per parse_ini_file() einlesen?

        Schritt für Schritt:

        die ini hat mtime 1:1:1

        1. Aufruf (voller Prozess):
        index.php liest ini ein und sendet eine Response mit 1:1:1 als lastmod
        Browser speichert die Seite ab mit 1:1:1 als lastmod

        2. Aufruf
        Browser sendet 1:1:1 im Header mit und fragt somit den Server, ob sich die Seite geändert hat
        Server guckt nach mtime der ini und siehe da, es ist dieselbe
        Server sendet einen Status: 304 Not Modified und bricht das weitere Senden sowie den Prozess ab (wenns gut programmiert ist)
        Browser holt die Seite aus dem Cache
        Besucher ist zufrieden
        Server spart Rechenleistung
        Du sparst Bandbreite

        mtime muss natürlich RFC gerecht formatiert sein 1:1:1 ist nurn Beispiel.

        Das richtige Format sieht so aus:
        Last-Modified: Thu, 13 Aug 2009 08:37:55 GMT

        Die Anfrage des Browsers dazu ist in der CGI-Umg-Variable HTTP_IF_MODIFIED_SINCE zu finden.

        Hotte

  2. Hello,

    ich glaube, dass Hotti Dich falsch verstanden hat.

    Deine index.php soll doch wohl jedes Mal abgearbeitet werden?
    Die Initialisierungsdaten könntest Du in einem Shared-Memory-Bereich ablegen. Wenn sie für alle User gleich sind, muss es den ja nur einmal geben.

    http://de3.php.net/manual/en/book.sem.php
    http://de3.php.net/manual/en/book.shmop.php

    Liebe Grüße aus dem schönen Oberharz

    Tom vom Berg

    --
    Nur selber lernen macht schlau
    http://bergpost.annerschbarrich.de
    1. Hello Tom,

      ich glaube, dass Hotti Dich falsch verstanden hat.

      ja, das vermute ich auch mal.

      Deine index.php soll doch wohl jedes Mal abgearbeitet werden?

      Exakt!

      Die Initialisierungsdaten könntest Du in einem Shared-Memory-Bereich ablegen. Wenn sie für alle User gleich sind, muss es den ja nur einmal geben.

      http://de3.php.net/manual/en/book.sem.php
      http://de3.php.net/manual/en/book.shmop.php

      Danke für den Tipp.
      Allerdings müsste ich dann wohl erstmal "üben", wie ich selber eine passende PHP Version kompiliere, da die beiden Feature ja standardmäßig nicht enthalten sind. Aktuell verwende ich immer die fertigen Debian (5) Pakete.

      Die Frage, die sich mir dann weiterhin stellt ist, ob sich der "Aufwand" für die paar Seitenaufrufe am Tag wirklich lohnt? Es handelt sich ja nicht um eine "high-performance" Website mit hunderten gleichzeitiger Zugriffe.

      Gruß Gunther

      1. Hello,

        Hello Tom,

        ich glaube, dass Hotti Dich falsch verstanden hat.
        ja, das vermute ich auch mal.

        Deine index.php soll doch wohl jedes Mal abgearbeitet werden?
        Exakt!

        Die Initialisierungsdaten könntest Du in einem Shared-Memory-Bereich ablegen. Wenn sie für alle User gleich sind, muss es den ja nur einmal geben.

        http://de3.php.net/manual/en/book.sem.php
        http://de3.php.net/manual/en/book.shmop.php
        Danke für den Tipp.
        Allerdings müsste ich dann wohl erstmal "üben", wie ich selber eine passende PHP Version kompiliere, da die beiden Feature ja standardmäßig nicht enthalten sind. Aktuell verwende ich immer die fertigen Debian (5) Pakete.

        Hast Du das überprüft, ob die Funktionen nicht drin sind?
        Bei mir sind sie nämlich drin:

        [129] => shm_attach
                    [131] => shm_detach
                    [133] => shm_get_var
                    [132] => shm_put_var
                    [130] => shm_remove
                    [134] => shm_remove_var
                    [705] => shmop_close
                    [708] => shmop_delete
                    [703] => shmop_open
                    [704] => shmop_read
                    [706] => shmop_size
                    [707] => shmop_write
                    [371] => show_source

        Die Frage, die sich mir dann weiterhin stellt ist, ob sich der "Aufwand" für die paar Seitenaufrufe am Tag wirklich lohnt? Es handelt sich ja nicht um eine "high-performance" Website mit hunderten gleichzeitiger Zugriffe.

        Nein, der Aufwand lohnt sich erst ab einigen Hundert Seitenaufrufen pro Sekunde. Es funktioniert auch nur, wenn Du volle Gewalt über den Server hast und alles so konfigurieren darfst, wie Du willst. Du benötigst ein Kontrollscript dafür, dass als "Master" diesen Shared-Memory-Bereich hütet und die Zugriffe darauf verwaltet. Und Du musst Dir überlegen, wie Du den Identifier an die einzelnen Prozesse weitergibst. Jeder request ist ja schließlich ein eigener Prozess.

        Liebe Grüße aus dem schönen Oberharz

        Tom vom Berg

        --
        Nur selber lernen macht schlau
        http://bergpost.annerschbarrich.de
        1. Hello,

          Hast Du das überprüft, ob die Funktionen nicht drin sind?
          Bei mir sind sie nämlich drin:

          ja du hast Recht - bei mir auch. War gestern wohl zu spät und so habe ich irrtümlich nur auf die PHP-Info meines lokalen (Windows) Servers geguckt (XAMPP), und da sind sie nicht enthalten. Auf meinem Webserver (Linux) sind sie ebenfalls beide vorhanden und aktiviert.

          Die Frage, die sich mir dann weiterhin stellt ist, ob sich der "Aufwand" für die paar Seitenaufrufe am Tag wirklich lohnt? Es handelt sich ja nicht um eine "high-performance" Website mit hunderten gleichzeitiger Zugriffe.

          Nein, der Aufwand lohnt sich erst ab einigen Hundert Seitenaufrufen pro Sekunde.

          Das scheint mir wohl auch so - insbesondere nach den Benchmarks, die ich mit den anderen Methoden ermittelt habe.

          Solche Dinge überlasse ich dann (doch erstmal) lieber Leuten, die echte high-performance Websites brauchen. Aber trotzdem besten Dank für die Hinweise - war mir nämlich bis dato unbekannt.

          Gruß Gunther

          1. Hello,

            Nein, der Aufwand lohnt sich erst ab einigen Hundert Seitenaufrufen pro Sekunde.
            Das scheint mir wohl auch so - insbesondere nach den Benchmarks, die ich mit den anderen Methoden ermittelt habe.

            Solche Dinge überlasse ich dann (doch erstmal) lieber Leuten, die echte high-performance Websites brauchen. Aber trotzdem besten Dank für die Hinweise - war mir nämlich bis dato unbekannt.

            Immer dann, wenn komplizierte und damit zeitaufwändige Berechnungen und/oder Datenbeschaffungsmaßnahmen zugrunde liegen, die aber nicht jedes Mal durchgeführt werden müssen, sondern nur, wenn sich am Datenstamm etwas geändert hat, dann lohnt es sich, das Zwischenergebnis zu cachen.

            Und dann merkst Du auch den Unterschied.

            Liebe Grüße aus dem schönen Oberharz

            Tom vom Berg

            --
            Nur selber lernen macht schlau
            http://bergpost.annerschbarrich.de
            1. Hello,

              Immer dann, wenn komplizierte und damit zeitaufwändige Berechnungen und/oder Datenbeschaffungsmaßnahmen zugrunde liegen, die aber nicht jedes Mal durchgeführt werden müssen, sondern nur, wenn sich am Datenstamm etwas geändert hat, dann lohnt es sich, das Zwischenergebnis zu cachen.

              das mag ja sicherlich vom Grundsatz her richtig sein, aber die Frage ist halt immer noch, ab wann eine Berechnung und/oder Datenbeschaffungsmaßnahme "kompliziert und damit zeitaufwändig" ist? Bei Unterschieden von einer halben Millisekunde pro Request, ist das für mich jedenfalls kein wirklicher Grund, einen wesentlich höheren Aufwand zu betreiben.

              Zumal ich, wenn auch eher intuitiv als wissend, der Meinung bin, dass die ohnehin vorhandenen Caching-Mechanismen (Apache, PHP, MySQL, etc.) meist völlig ausreichend sind für "normale" Websites und es schon "besonderer" Umstände bedarf, bevor es sinnvoll ist, diese (ggf. sogar zugunsten eigener Mechanismen) zu erweitern/ ersetzen.

              Gruß Gunther

              1. Hello,

                das mag ja sicherlich vom Grundsatz her richtig sein, aber die Frage ist halt immer noch, ab wann eine Berechnung und/oder Datenbeschaffungsmaßnahme "kompliziert und damit zeitaufwändig" ist? Bei Unterschieden von einer halben Millisekunde pro Request, ist das für mich jedenfalls kein wirklicher Grund, einen wesentlich höheren Aufwand zu betreiben.

                Das kannst Du so einfach leider gar nicht betrachten. Solange der Rechner genügend Leistungsreserve hat, wirst Du keine besonders negativen Auswirkungen feststellen. Erst, wenn die Kollisionen häufiger werden, dann steigt die verbrauchte Rechenzeit durch die gegenseitigen Sperren an, wächst also nicht mehr linear, sondern eher exponientiell.

                Und dann ist jede vermeidbare Abfrage z.B. auf das Filesystem oder auf die Datenbank eine kostbare Einsparungsmöglichkeit. Warum sollte man noch abfragen und rechnen, wenn man das Ergebnis bereits cached hat?

                Liebe Grüße aus dem schönen Oberharz

                Tom vom Berg

                --
                Nur selber lernen macht schlau
                http://bergpost.annerschbarrich.de
  3. Moin!

    Frage:
    Da sich im Normalfall an den Daten in der config.ini nicht allzuhäufig allzuviel ändert, gibt es da eigentlich eine performantere Lösung?

    Die INI-Datei in ein Stück PHP-Source wandeln, der die Variablen in identischer Weise definiert.

    Dieser Code wird dann mittels include() eingebunden und hat den Vorteil, dass das Parsing-Ergebnis sich auch in Opcode-Caches wie APC ablegt, also bei weiteren Aufrufen extrem viel schneller verfügbar ist.

    Da es sich bei den (eingelesenen) Daten ja dann um ein mehrdimensionales Array handelt, könnte/ müsste ich es serialisieren und dann ebenfalls als Datei speichern. Und umgekehrt ebenfalls erst wieder den Dateiinhalt einlesen und anschließend deserialisieren.

    Ich sehe da vom Grundsatz her eigentlich keinen Unterschied zwischen den beiden Varianten. Oder ist eine der beschriebenen Methoden signifikant schneller als die andere?

    Wenn du keinen Unterschied siehst: Miß die Zeit, die zum Ausführen des jeweiligen Codes notwendig ist. Die Uhr wird mutmaßlich einen Unterschied sehen.

    Bedenke aber, dass die Zeitmessung außerhalb von PHP erfolgen muss, wenn du die Zeit für das Parsen des Codes mit erfassen musst.

    - Sven Rautenberg

    1. Moin Sven!

      Frage:
      Da sich im Normalfall an den Daten in der config.ini nicht allzuhäufig allzuviel ändert, gibt es da eigentlich eine performantere Lösung?

      Die INI-Datei in ein Stück PHP-Source wandeln, der die Variablen in identischer Weise definiert.

      Also bspw. in das bereits erwähnte multidimensionale Array, richtig?

      Dieser Code wird dann mittels include() eingebunden und hat den Vorteil, dass das Parsing-Ergebnis sich auch in Opcode-Caches wie APC ablegt, also bei weiteren Aufrufen extrem viel schneller verfügbar ist.

      Gilt das auch für das serialisierte Array (welches sich dann in einer separaten Datei auf dem Server befindet)?

      Ich sehe da vom Grundsatz her eigentlich keinen Unterschied zwischen den beiden Varianten. Oder ist eine der beschriebenen Methoden signifikant schneller als die andere?

      Wenn du keinen Unterschied siehst: Miß die Zeit, die zum Ausführen des jeweiligen Codes notwendig ist. Die Uhr wird mutmaßlich einen Unterschied sehen.

      Bedenke aber, dass die Zeitmessung außerhalb von PHP erfolgen muss, wenn du die Zeit für das Parsen des Codes mit erfassen musst.

      OK, das leuchtet mir soweit ein. Stellt sich mir aber gleich die nächste Frage, nämlich wie messe ich denn die Zeit außerhalb von PHP?

      Gruß Gunther

      1. Hi!

        Die INI-Datei in ein Stück PHP-Source wandeln, der die Variablen in identischer Weise definiert.
        Also bspw. in das bereits erwähnte multidimensionale Array, richtig?

        So wie das Ergebnis von parse_ini() ist oder eben so, wie du das am besten gebrauchen kannst.

        Dieser Code wird dann mittels include() eingebunden und hat den Vorteil, dass das Parsing-Ergebnis sich auch in Opcode-Caches wie APC ablegt, also bei weiteren Aufrufen extrem viel schneller verfügbar ist.
        Gilt das auch für das serialisierte Array (welches sich dann in einer separaten Datei auf dem Server befindet)?

        Nein, serialisierte Daten sind ein String, den du irgendwo ablegen musst und der zur Laufzeit analysiert und in Variablen umgewandelt werden muss. OP-Code-Caches halten sozusagen ein Speicherabbild des lauffähigen Codes bereit.

        Bedenke aber, dass die Zeitmessung außerhalb von PHP erfolgen muss, wenn du die Zeit für das Parsen des Codes mit erfassen musst.
        OK, das leuchtet mir soweit ein. Stellt sich mir aber gleich die nächste Frage, nämlich wie messe ich denn die Zeit außerhalb von PHP?

        Für deinen Besucher ist die Zeit relevant, die er bis zum Ausliefern warten muss. Insgesamt ist interessant, wieviele Besucher deine Seite pro Zeiteiheit verarbeiten kann. Also musst du ein Tool nehmen, das User-Requests simuliert, beispielsweise Apache-Benchmark (ab) - ist im Prinzip bei jedem Apache dabei.

        Lo!

  4. Hallo Selfer/innen!

    Erstmal meinen besten Dank für die fachlich (wie hier eigentlich immer) sehr kompente und auch (für mich) verständliche Hilfe.

    Aufgrund des Postings von Sven und der zusätzlichen Erklärung von dedlfix, habe ich mal die Benchmarks für die zwei folgenden Varianten ermittelt:

    Variante 1:

      
    <?php  
    $config = parse_ini_file('php_test.ini');  
    var_dump($config);  
    ?>  
    
    

    Variante 2:

      
    <?php  
    include('config_file.php');  
    var_dump($config);  
    ?>  
    
    

    Wobei ich als INI-Datei meine php.ini auf dem Server verwendet habe. Die Datei 'config_file.php' ist eine automatisch generierte Datei aus dieser INI-Datei, die einfach die Definition des Arrays $config enthält.

    Ich habe ab mit jeweils 10.000 Requests mehrmals ausgeführt, da immer leichte Schwankungen auftreten, aber die nachfolgenden Ergebnisse repräsentieren das durchschnittliche Ergebnis:

    ----------------------------------------------------------------------------------------------
    Variante 1 (parse_ini_file):
    Finished 10000 requests

    Server Software:        Apache
    Server Port:            80

    Document Path:          /benchm1.php
    Document Length:        6570 bytes

    Concurrency Level:      1
    Time taken for tests:   13.888 seconds
    Complete requests:      10000
    Failed requests:        0
    Write errors:           0
    Total transferred:      67800000 bytes
    HTML transferred:       65700000 bytes
    Requests per second:    720.03 [#/sec] (mean)
    Time per request:       1.389 [ms] (mean)
    Time per request:       1.389 [ms] (mean, across all concurrent requests)
    Transfer rate:          4767.42 [Kbytes/sec] received

    Connection Times (ms)
                  min  mean[+/-sd] median   max
    Connect:        0    0   0.3      0       4
    Processing:     0    1   2.0      0      40
    Waiting:        0    1   1.9      0      40
    Total:          0    1   2.0      0      40

    Percentage of the requests served within a certain time (ms)
      50%      0
      66%      0
      75%      4
      80%      4
      90%      4
      95%      4
      98%      4
      99%      4
     100%     40 (longest request)

    ----------------------------------------------------------------------------------------------
    Variante 2 (include):
    Finished 10000 requests

    Server Software:        Apache
    Server Port:            80

    Document Path:          /benchm2.php
    Document Length:        6570 bytes

    Concurrency Level:      1
    Time taken for tests:   9.204 seconds
    Complete requests:      10000
    Failed requests:        0
    Write errors:           0
    Total transferred:      67800000 bytes
    HTML transferred:       65700000 bytes
    Requests per second:    1086.47 [#/sec] (mean)
    Time per request:       0.920 [ms] (mean)
    Time per request:       0.920 [ms] (mean, across all concurrent requests)
    Transfer rate:          7193.60 [Kbytes/sec] received

    Connection Times (ms)
                  min  mean[+/-sd] median   max
    Connect:        0    0   0.3      0       4
    Processing:     0    1   1.7      0      12
    Waiting:        0    1   1.6      0      12
    Total:          0    1   1.7      0      12

    Percentage of the requests served within a certain time (ms)
      50%      0
      66%      0
      75%      0
      80%      4
      90%      4
      95%      4
      98%      4
      99%      4
     100%     12 (longest request)

    ----------------------------------------------------------------------------------------------

    Auch wenn das Ergebnis imho zeigt, dass für "normal" besuchte Sites beide Methoden durchaus praktikabel sind, so wird doch auch ganz klar deutlich, dass die Include-Variante im Schnitt etwa um ein Drittel schneller ist.

    Vielen Dank nochmal an Alle, die mir wieder ein Stückchen weiter geholfen haben, was meine Kenntnisse und mein Wissen im Bezug auf PHP und Programmiertechnik anbelangt!

    Gruß Gunther

    1. Hallo Selfer/innen!

      So, ich habe jetzt auch noch eine dritte Variante ausprobiert, nämlich die mit dem serialisierten String:

      Variante 3:

        
      <?php  
      $config = unserialize(file_get_contents('config_file_ser.php'));  
      var_dump($config);  
      ?>  
      
      

      Benchmark Ergebnis:
      Finished 10000 requests

      Server Software:        Apache
      Server Port:            80

      Document Path:          /benchm3.php
      Document Length:        6570 bytes

      Concurrency Level:      1
      Time taken for tests:   7.972 seconds
      Complete requests:      10000
      Failed requests:        0
      Write errors:           0
      Total transferred:      67800000 bytes
      HTML transferred:       65700000 bytes
      Requests per second:    1254.37 [#/sec] (mean)
      Time per request:       0.797 [ms] (mean)
      Time per request:       0.797 [ms] (mean, across all concurrent requests)
      Transfer rate:          8305.30 [Kbytes/sec] received

      Connection Times (ms)
                    min  mean[+/-sd] median   max
      Connect:        0    0   0.3      0       4
      Processing:     0    1   1.5      0       8
      Waiting:        0    0   0.3      0       4
      Total:          0    1   1.6      0       8

      Percentage of the requests served within a certain time (ms)
        50%      0
        66%      0
        75%      0
        80%      0
        90%      4
        95%      4
        98%      4
        99%      4
       100%      8 (longest request)

      Und siehe da, diese Variante ist nochmal schneller als die Include-Methode!
      Auch wenn wir hier durchschnittlich nur etwa von einer zehntel Millisekunde reden.
      (Um evt. Unterschiede durch andere Serverauslastung zu vermeiden, habe ich die anderen Tests auch mehrfach wiederholt - sie lieferten dieselben Ergebnisse wie zuvor.)

      Gruß Gunther

    2. hi,

      Auch wenn das Ergebnis imho zeigt, dass für "normal" besuchte Sites beide Methoden durchaus praktikabel sind, so wird doch auch ganz klar deutlich, dass die Include-Variante im Schnitt etwa um ein Drittel schneller ist.

      Und wenn Du jetzt noch das Cachen mit Last-Modified richtig einbaust, wird die config.ini gar nicht erst in den Speicher geladen.

      Hotti

      1. Hi,

        Auch wenn das Ergebnis imho zeigt, dass für "normal" besuchte Sites beide Methoden durchaus praktikabel sind, so wird doch auch ganz klar deutlich, dass die Include-Variante im Schnitt etwa um ein Drittel schneller ist.

        Und wenn Du jetzt noch das Cachen mit Last-Modified richtig einbaust, wird die config.ini gar nicht erst in den Speicher geladen.

        ja und genau hier sind wir ja wieder an dem Punkt, wo sich der Hund in den eigenen Schwanz beißt! ;-)

        Wenn man sich nicht 100%ig darauf verlassen kann, dass die zu inkludierende Datei auf jeden Fall immer aktuell ist und demzufolge vorher jedes Mal eine Prüfung (bspw. über last modified) vornehmen muss, kann es auch sein, dass der eigentliche Geschwindigkeitsvorteil dadurch wieder futsch ist.

        Und auf ein ganzes Projekt gesehen, darf man auch den zusätzlichen Programmieraufwand (und dadurch auch die Erhöhung potentieller Fehlerquellen) nicht außer Acht lassen. Wenn dadurch dann Ende eine Zeitersparnis von ein paar Millisekunden steht, dann halte ich das speziell für mein aktuelles Vorhaben für nicht erforderlich.

        Wobei ich damit keinesfalls sagen will, dass es nicht an anderer Stelle durchaus Sinn macht. Wie gesagt, high-performance ist eben bei mir keine zwingende Notwendigkeit. Trotzdem finde ich es aber immer wichtig & interessant, die verschiedenen Methoden und ihre jeweiligen Vor- und Nachteile zu kennen. Deshalb frage ich dann hier im Forum nach, und freue mich auch immer, wenn ich dann wie hier so informative und nützliche Hinweise bekomme.

        Gruß Gunther

      2. Hi!

        Und wenn Du jetzt noch das Cachen mit Last-Modified richtig einbaust, wird die config.ini gar nicht erst in den Speicher geladen.

        Du bist auf der falschen Baustelle. Es geht nicht um das Ausliefern an einen Browser, sondern um das Erstellen eines veränderlichen Dokuments. Es nützt nichts, Last-Modified-Header an Browser zu senden, um damit das Neuladen der Seite zu verhindern, wenn gerade das Neuladen aufgrund sich ständig wechselnden Inhalts gefragt ist. Es geht darum, serverintern das Erstellen der Seite zu optimieren.

        Lo!