• PHP Prozessor optimiert kompilieren – ein Performance Vorteil oder Mythos ?

    von Axel Sauerhöfer am 17. November 2008

    Jeder LAMP Administrator steht irgendwann vor der Entscheidung, in welcher Form er PHP installieren möchte. Es gibt zum einen fertige Pakete, die bei jeder halbwegs modernen Linux Distribution mitgeliefert werden. Auf der anderen Seite gibt es noch die gute alte Methode, PHP aus dem Quellcode zu kompilieren. Beide Varianten haben Vor- und Nachteile.

    Der klare Vorteil für fertige Pakete ist die schnelle und einfache Installation bzw. Deinstallation. Der Nachteil ist die Konfiguration. Man hat leider keinen Einfluss darauf, was zum Beispiel als Modul oder was als „Built in“ geliefert wird.

    Ein anderes Thema, welches immer wieder gerne diskutiert wird, ist der angebliche Performance Vorteil bei selbst erstellten Kompilaten aus dem Quellcode. Da man bei diesen die letzte Prozessor-Optimierung ausnutzen kann. Dabei muss keine Rücksicht darauf genommen werden, ob das fertige PHP noch auf anderen Plattformen lauffähig ist.

    Anhand eines Fallbeispiels versuche ich ein wenig Klarheit ins dunkle zu bringen. Dazu werde ich einmal PHP als Paket und einmal aus dem Quellcode installieren. Dazu dient eine neue Debian 4.0 Gnu/Linux Installation als Basis.

    Auf PHP Accelatoren wird bewusst nicht in diesem Artikel eingegangen, da dieses Thema genug Material für einen eigenen bietet. Der hoffentlich bald von mir geschrieben wird.

    Technische Server Daten:

    cat /proc/cpuinfo

    processor : 0
    vendor_id : AuthenticAMD
    cpu family : 15
    model : 39
    model name : AMD Opteron(tm) Processor 148
    stepping : 1
    cpu MHz : 2195.418
    cache size : 1024 KB
    fdiv_bug : no
    hlt_bug : no
    f00f_bug : no
    coma_bug : no
    fpu : yes
    fpu_exception : yes
    cpuid level : 1
    wp : yes
    flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov
    pat pse36 clflush mmx fxsr sse sse2 pni syscall nx mmxext fxsr_opt lm 3dnowext 3 dnow pni
    bogomips : 4391.99

    cat /proc/meminfo

    MemTotal: 2067832 kB
    MemFree: 86092 kB
    Buffers: 203192 kB
    Cached: 755372 kB
    SwapCached: 56 kB
    Active: 986748 kB
    Inactive: 642788 kB
    HighTotal: 0 kB
    HighFree: 0 kB
    LowTotal: 2067832 kB
    LowFree: 86092 kB
    SwapTotal: 3068392 kB
    SwapFree: 3065036 kB
    Dirty: 124 kB
    Writeback: 0 kB
    Mapped: 793220 kB
    Slab: 320256 kB
    CommitLimit: 4102308 kB
    Committed_AS: 2016668 kB
    PageTables: 12544 kB
    VmallocTotal: 2039800 kB
    VmallocUsed: 9164 kB
    VmallocChunk: 2030440 kB

    Der Test:
    Bench.php von Sebastian Bergmann (http://sebastian-bergmann.de/archives/634-PHP-GCC-ICC-Benchmark.html).

    Die Tests werden ausschließlich auf der Kommandozeile ausgeführt, um eventuelle Beeinflussung der Testergebnisse durch Netzwerkprobleme auszuschließen.

    Die Durchführung:

    Der Benchmark wird jeweils 5 mal durchgeführt, daraus wird dann der Mittelwert gebildet.

    Kandidat 1: Das fertige PHP Paket

    Die Installation könnte einfacher nicht sein, apt sein dank:

    ~# apt-get install php5-cli
    ~# php -v

    PHP 5.2.0-8+etch13 (cli) (built: Oct 2 2008 08:26:18)
    Copyright (c) 1997-2006 The PHP Group
    Zend Engine v2.2.0, Copyright (c) 1998-2006 Zend Technologies

    Konfiguration: php.ini

    An den Standardeinstellung der php.ini werden keine Änderungen vorgenommen.

    Durchführung und Ergebnisse:

    ~#php bench.php

    Lauf 1 Lauf 2 Lauf 3 Lauf 4 Lauf 5 Average
    Simple 1.078 1.119 1.124 1.039 1.123 1.096
    Simplecall 1.262 1.213 1.320 1.302 1.322 1.283
    Simpleucall 1.839 1.858 1.865 1.822 1.861 1.849
    Simpleudcall 2.221 2.204 2.683 2.909 2.317 2.466
    Mandel 3.279 3.305 4.712 3.291 3.268 3.571
    mandel2 4.188 4.193 4.311 4.163 4.164 4.203
    ackermann(7) 2.578 2.687 2.532 2.575 2.661 2.606
    ary(50000) 0.160 0.157 0.158 0.159 0.157 0.1582
    ary2(50000) 0.129 0.127 0.133 0.124 0.128 0.1282
    ary3(2000) 2.066 1.983 1.818 1.848 1.907 1.9244
    fibo(30) 5.598 5.611 5.728 5.713 5.663 5.6626
    hash1(50000) 0.327 0.301 0.307 0.293 0.298 0.3052
    hash2(500) 0.283 0.277 0.286 0.277 0.271 0.2788
    heapsort(20000) 0.863 0.848 0.885 0.931 0.889 0.8832
    matrix(20) 0.766 0.746 0.735 0.768 0.780 0.7590
    nestedloop(12) 1.884 1.936 1.910 1.872 1.878 1.8960
    sieve(30) 0.766 0.793 0.792 0.839 0.840 0.8060
    strcat(200000) 0.306 0.309 0.309 0.302 0.350 0.3152
    Total 29.592 29.666 31.608 30.227 29.878 30.19

    Zum vergrößern, einfach auf Bild klicken

    Kandidat 2: PHP aus dem Quellcode

    Um keine verfälschten Messergebnisse zu bekommen, lade ich den etwas veralteten Quellcode von PHP 5.2.0 herunter. Dieser entspricht der Paketversion:

    http://www.php.net/releases/

    ~# cd /usr/local/src
    ~# wget http://museum.php.net/php5/php-5.2.0.tar.bz2
    ~# bunzip2 php-5.2.0.tar.bz2
    ~# tar -xvf php-5.2.0.tar
    ~# apt-get install build-essential gcc flex bison libxml2-dev

    Abhängig von der CPU ( siehe /proc/cpuinfo ) müssen jetzt die CFLAGS exportiert werden, in unserem Fall werden folgende verwendet:

    ~# export CHOST=”i686-pc-linux-gnu”
    ~# export CFLAGS=”-O3 -mtune=opteron -march=opteron -mcpu=opteron -mmmx -msse -msse2 -mfpmath=sse -fforce-addr -fomit-frame-pointer -foptimize-sibling-calls -funroll-loops -frerun-cse-after-loop -frerun-loop-opt -falign-functions=4″
    ~# export CXXFLAGS=$CFLAGS

    Die optimalen Schalter für jede CPU können in der Compilerdokumentation entnommen werden:

    http://gcc.gnu.org/onlinedocs/gcc/
    http://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html#Optimize-Options

    ~# ./configure –prefix=/usr/local/php-5.2.0
    ~# make
    ~# make install

    Prüfen auf richtige Version.

    ~# /usr/local/php-5.2.0/bin/php -v
    PHP 5.2.0 (cgi) (built: Nov 12 2008 18:25:37)
    Copyright (c) 1997-2006 The PHP Group
    Zend Engine v2.2.0, Copyright (c) 1998-2006 Zend Technologies

    Konfiguration: php.ini

    An den Standardeinstellung der php.ini werden keine Änderungen vorgenommen.

    Durchführung und Ergebnisse:

    ~# /usr/local/php-5.2.0/bin/php bench.php

    Lauf 1 Lauf 2 Lauf 3 Lauf 4 Lauf 5 Average
    Simple 0.788 0.786 0.791 0.784 0.789 0.787
    Simplecall 0.842 0.861 0.842 0.835 0.858 0.847
    Simpleucall 1.434 1.437 1.413 1.430 1.452 1.433
    Simpleudcall 1.625 1.661 1.647 1.654 1.660 1.649
    Mandel 2.218 2.214 2.216 2.212 2.202 2.212
    mandel2 2.810 2.833 2.834 2.855 2.811 2.828
    ackermann(7) 1.961 1.940 1.825 2.125 1.852 1.940
    ary(50000) 0.140 0.139 0.140 0.138 0.144 0.140
    ary2(50000) 0.109 0.108 0.106 0.107 0.107 0.107
    ary3(2000) 1.378 1.503 1.583 1.526 1.543 1.506
    fibo(30) 4.167 4.136 4.179 4.152 4.143 4.155
    hash1(50000) 0.201 0.186 0.212 0.194 0.199 0.198
    hash2(500) 0.204 0.203 0.508 0.569 0.206 0.338
    heapsort(20000) 0.660 0.666 0.652 0.965 0.737 0.736
    matrix(20) 0.601 0.587 0.585 0.594 0.584 0.590
    nestedloop(12) 1.431 1.431 1.436 1.445 1.443 1.437
    sieve(30) 0.684 0.692 0.696 0.641 0.689 0.680
    strcat(200000) 0.088 0.089 0.088 0.087 0.090 0.088
    Total 21.340 21.473 21.753 22.315 21.508 21.67

    Zum vergrößern, einfach auf Bild klicken

    Das Fazit:

    Stellt man beide Testergebnisse nebeneinander, sieht man eine enormen Performancegewinn von durchschnittlich zirka 9 Sekunden. Dies ergibt einen Faktor von zirka 1,39.

    Quellcode Paket
    Simple 0.787 1.096
    Simplecall 0.847 1.283
    Simpleucall 1.433 1.849
    Simpleudcall 1.649 2.466
    Mandel 2.212 3.571
    mandel2 2.828 4.203
    ackermann(7) 1.940 2.606
    ary(50000) 0.140 0.1582
    ary2(50000) 0.107 0.1282
    ary3(2000) 1.506 1.9244
    fibo(30) 4.155 5.6626
    hash1(50000) 0.198 0.3052
    hash2(500) 0.338 0.2788
    heapsort(20000) 0.736 0.8832
    matrix(20) 0.590 0.7590
    nestedloop(12) 1.437 1.8960
    sieve(30) 0.680 0.8060
    strcat(200000) 0.088 0.3152
    Total 21.67 30,19

    Zum vergrößern, einfach auf Bild klicken

    Wer genug Zeit und Motivation mitbringt, kann durch ein selbstkompiliertes PHP, Performance gewinnen. Jedoch bin ich der Meinung das diese Art der Optimierung einer der letzten Schritte sein sollte, zuvor müssen die PHP Skripte und SQL Statements optimiert werden.

    Axel Sauerhöfer

    http://willcodejoomlaforfood.de

    Zum Profil von Axel Sauerhöfer

    3 Kommentare »


    • Nils Langner
      am 17. November 2008 um 13:00 Uhr

      Sind auf jeden Fall beeindruckende Zahlen. Hätte nie gedacht, dass hier wirklich ein Faktor von 1,4 rauszuholen ist. Nur leider ist das Problem, dass man nicht immer Herr des eigenen Servers ist und man nicht die Wahl hat.


    • Johannes
      am 17. November 2008 um 18:58 Uhr

      Ein paar Dinge sind dabei zu berücksichtigen:

      a) Manche Distributoren sind schlauer als andere und verwenden bessere Optimierungen, Dinge die Otto-Normal-Anwender nicht kennt. Dadurch kann es schneller sein. Gibt aber auch Distributoren, die PHP blöd finden und das nur irgendwie kompilieren ;-)

      b) Manche Distributoren patchen PHP, teils backporting von Security Dingen, teils Suhoshin patch, teils eigene “kreative” Dinge, hat definitv auswirkungen in die eine oder andere Richtungen

      c) bench.php is ein Engine-Benchmark, das für Engine-Entwickler gedacht ist um bestimmte Kernteile der Engine zu testen und dort Regressionen zu entdecken. Es hat nichts mit real-life zu tun. Es nutzt z.B. keinen extension Code (also für die einzelnen tests, für sowas wie time() schon, aber das is zu ignorieren) nun wollen Distributoren flexibel sein machen also aus allen Extensions eigene packages die dann per php.ini geladen werden. Dies hat “massive” Kosten beim Start, was insbesondere bei CGI (ok, wer CGI nutzt will keine Performance (FastCGI ist _nicht_ gemeint)) Auswirkungen hat und _kann_ auch Auswirkungen auf PHP-Performance zur Laufzeit haben.

      d) Die PHP Performance hängt nicht nur an PHP selber sondern auch an allen libs die genutzt werden, da wird evtl. auch naders kompiliert

      e) Neben der single-prozess-Geschwindigkeit muss man auch Dinge wie z.B. Speicherverbrauch ansehen – wenn ein Kompilat deutlich mehr Speicher verbraucht bekomme ich davon weniger auf eine Ksite -> geringere Performance

      f) Die Unterschiede sind, in einem realen System, wo 80% der Zeit mit warten auf Datenbank und sonstige Netzwerkoperationen (Daten an Client z.B.) verbracht werden am Ende nicht messbar, wohingegen einfache Administration und regelmäßige “automatische” Sicherheitsupdates messbar sind.

      g) Oh und das Thema Support – Firmen wie Zend, RedHat und Sun bieten kommerziellen Support für ihre Builds – wenn man also jemanden zur Hand haben will wenn es kracht kann das auch was Wert sein


    • Axel
      am 17. November 2008 um 19:52 Uhr

      Hi Johannes,

      ich gebe dir Recht, im “Real Life” spielen viele andere Faktoren eine viel größere Rolle als das PHP Binary. Wie schon erwähnt anständige SQL Statments, oder einen Apache anständig als “mpm worker” konfiguriert ;)

      Ich habe absichtlich die Debian Distri gewählt, da ich dort den “krassesten” Unterschied verzeichnen konnte :D

      gruss axel

    RSS-Feed für Kommentare zu diesem Artikel. TrackBack-URL

    Einen Kommentar hinterlassen

    Werbung
    PHP Magazin
    Ausgabe 02/2010

    Dieses Mal mit Artikeln zu den Themen OpenSocial und Apache Shindig, Graphentheorie, Smarty3

    t3n
    Ausgabe 19

    Social Media (R)evolution. Weitere Themen sind noSQL, Crowdsourcing ...

    PHP Journal
    Ausgabe 2/2010

    PHP & Windows optimal nutzen, die besten PHP-CMS im Überblick, Google-API mit Zend Framework nutzen.

    Wir wurden schon öfters gefragt, ob man uns nicht irgendwie unterstützen kann. Die Antwort war immer einfach: Klar! Am einfachsten ist es eure nächsten Einkäufe bei Amazon über unsere Link abzuwickeln. Damit würdet ihr uns schon sehr helfen. Über Co-Autoren freuen wir uns aber noch mehr.