Fabulit: Performance nach Lesbarkeitsoptimierung

Moin,

ich bin eher zufällig über Perfomance-Schwankungen gestolpert, die mir nicht so recht einleuchten. Es geht um den Vergleich zwischen:

Variante A

if(bedingung1) {  
	if(bedingung2) {  
		...  
	}  
}  

Variante B

if(bedingung1 && bedingung2) {  
	...  
}  

Ich bin bisher davon ausgegangen, dass es sich um nahezu gleichwertige Abfragen handelt, aber Variante B war in meinem Szenario 5 - 8 % langsamer.

Zusätzlich lagere ich komplexe und wiederkehrende Prüfungen auch gern in eigene Funktionen aus:

if(pruefe_bedingungen(bedingung1, bedingung2)) {  
	...  
}  
function pruefe_bedingungen(bedingung1, bedingung2) {  
	if(bedingung1) {  
		if(bedingung2) {  
			return true;  
		}  
	}  
	return false;	  
}  

Dies hat zur Folge, dass die Ausführungszeit sich _verdreifacht_. Ok, ich habe dadurch eine zusätzliche if-Abfrage und einen zusätzlichen Funktionsaufruf eingebaut, aber dass die Perfomance derart betroffen ist, betrübt mich. Ich gewinne grade den Eindruck, dass meine Optimierungen zur Lesbarkeit und Wartbarkeit des Codes immer mit Performance-Einbußen einhergehen könnten. Und auf meine Intuition bezüglich der Perfomance kann ich mich auch nicht verlassen.

Kann bitte jemand (mit einfachen Worten) etwas Klarheit in meine Verwirrung bringen. Oder muss man wirklich verstehen was hinter den Kulissen passiert, um die Perfomance einschätzen zu können?

Gruß Fabulit

  1. Hallo,

    Ich gewinne grade den Eindruck, dass meine Optimierungen zur Lesbarkeit und Wartbarkeit des Codes immer mit Performance-Einbußen einhergehen könnten.

    Es ist die Sache nicht wert, sich mit Mikro-Benchmarks und anschließenden Mikro-Optimierungen auseinander zu setzen. Selbst wenn du statistisch sinnvolle Benchmarks vornimmst, bekommst du Zahlen heraus, die dir über die Performance *deiner Anwendung* nichts sagen.

    Heutige Scriptsprachen-Interpreter optimieren den erzeugten Bytecode automatisch, wenn er häufig aufgerufen wird. Durch Techniken wie Function Inlining und Loop Unrolling sind Funktionen und Schleifen im Bytecode nicht mehr notwendig zu sehen. Händische, auf Intuition basierende Optimierungen können sogar schädlich für die Performance sein. Insbesondere auf dem Level, das du beschreibst.

    Optimiere Anwendungscode *immer* auf Lesbarkeit. Schreibe Code für Menschen. Nutze Funktionen, wie es dir für die Codestrukturierung und die Code-Wiederverwendbarkeit als sinnvoll erscheint.

    Eine generelle Strategie, wie man Performance optimiert, habe ich hier beschrieben:

    http://forum.de.selfhtml.org/archiv/2014/3/t216853/#m1488117

    Kurz gesagt: Identifiziere Performance-Probleme in der realen, laufenden Anwendung in deren konkreten Abläufen und Algorithmen auf hoher Ebene. Die Schreibweise von einzelnen if-Anweisungen und die Nutzung von Funktionen trägt natürlich zur Anwendungsperformance bei, aber es bringt nichts, sie rein formal und ohne Kontext zu betrachten.

    Mathias

    1. Hallo,

      vielen Dank für deine informativen Antworten und dein Fazit. Ich habe mich tatsächlich gefragt, ob es fahrlässig ist seinen Code *immer* auf Lesbarkeit zu optimieren.

      Heutige Scriptsprachen-Interpreter optimieren den erzeugten Bytecode automatisch...

      Dies stützt ja eigentlich meine Vermutung, dass zwei unterschiedliche Schreibweisen derselben Logik am Ende gleich (schnell) ausgeführt werden müssten. Aber gut, nun weiß ich dass es noch eine Blackbox im Hintergrund gibt.

      Deine Hinweise zum Optimieren sind auch aufschlussreich. Da ich keine Erfahrungen mit großen Anwendungen habe, frage ich mich allerdings wie man dort noch die Flaschenhälse identifizieren will - ich hatte bereits jetzt "Probleme". Nutzt man spezielle Tools, oder ist es "händische" Arbeit?

      Und steht man am Ende nicht wieder irgendwann vor der Frage, ob man die Code-Struktur optimieren sollte, selbst wenn man an der eigentlich Programm-Logik nichts mehr zu verbessern weiß?

      Gruß Fabulit

      1. Hallo,

        frage ich mich allerdings wie man dort noch die Flaschenhälse identifizieren will - ich hatte bereits jetzt "Probleme". Nutzt man spezielle Tools, oder ist es "händische" Arbeit?

        Ich bin kein PHP-Entwickler, aber ja, es gibt Tools, die deine Anwendung zur Laufzeit analysieren und dir genau aufschlüsseln, wo die Zeit verbraten wurde. Die heißen üblicherweise Profiler oder generell Performance-Analyzer.

        PHP ist an sich sehr schnell. Für PHP speziell gibt es Optimierungsverfahren wie z.B. Opcode-Caches:
        http://en.wikipedia.org/wiki/List_of_PHP_accelerators
        http://php.net/manual/en/book.apc.php
        und entsprechende Application-Server, z.B. den von Zend.

        Und steht man am Ende nicht wieder irgendwann vor der Frage, ob man die Code-Struktur optimieren sollte, selbst wenn man an der eigentlich Programm-Logik nichts mehr zu verbessern weiß?

        Saubere Code-Struktur (z.B. klassenbasierte OOP, Class-Autoloading, MVC-Frameworks…) erzeugt natürlich einen Overhead gegenüber Low-Level-Programmierung, ermöglicht dem Compiler aber auch Optimierungen. Die üblichen Methoden zur Code-Strukturierung, die auf Abstrahierung/Übersichtlichkeit, Lesbarkeit, Entkoppelung und Testbarkeit ausgerichtet sind, erzeugen keinen nennenswerten Overhead (mehr).

        Wenn doch, dann ist PHP wahrscheinlich nicht die Sprache der Wahl, sondern es muss eine statisch typisierte Sprache wie Hack, Java oder Go her. Am besten eine, die Concurrency schon eingebaut hat.

        Was üblicherweise bei Software Bottlenecks erzeugt ist, ist nicht die Code-Struktur allgemein, sondern die besagten Algorithmen sowie:

        • Input/Output
           - Lesen von der / Schreiben auf die Festplatte
           - Datenbankabfragen
           - Netzwerkverkehr
           - Objekte (de)serialisieren (Parsing, Marshalling)
        • Synchrone Aufgaben (erst muss A erledigt werden, dann kann B erledigt werden, wobei A lange dauert oder fehlschlagen kann)
        • Fehlende Parallelisierung (1000 Datensätze müssen verarbeitet werden, was parallel auf mehreren Kernen/Rechnern passieren könnte)

        Das lässt sich generell lösen durch:

        • Abhängigkeiten vermeiden
        • Wiederholungen vermindern
        • Optimierung der I/O: Caching, Vermeidung redundanter Anfragen, Nutzung optimierter Datenbanken
        • Asynchrone und verteilte Berechnungen (Map Reduce)

        Siehe bspw. http://highscalability.com/blog/2012/5/16/big-list-of-20-common-bottlenecks.html

        Grüße
        Mathias

  2. Hi,

    Variante B

    if(bedingung1 && bedingung2) {

    ...
    }

    
    >   
    > Zusätzlich lagere ich komplexe und wiederkehrende Prüfungen auch gern in eigene Funktionen aus:  
    >   
    > ~~~php
    
    if(pruefe_bedingungen(bedingung1, bedingung2)) {  
    
    > 	...  
    > }  
    > function pruefe_bedingungen(bedingung1, bedingung2) {  
    > 	if(bedingung1) {  
    > 		if(bedingung2) {  
    > 			return true;  
    > 		}  
    > 	}  
    > 	return false;	  
    > }  
    > 
    
    

    Dies hat zur Folge, dass die Ausführungszeit sich _verdreifacht_. Ok, ich habe dadurch eine zusätzliche if-Abfrage und einen zusätzlichen Funktionsaufruf eingebaut, aber dass die Perfomance derart betroffen ist, betrübt mich. Ich gewinne grade den Eindruck, dass meine Optimierungen zur Lesbarkeit und Wartbarkeit des Codes immer mit Performance-Einbußen einhergehen könnten. Und auf meine Intuition bezüglich der Perfomance kann ich mich auch nicht verlassen.

    Unabhängig von dem, was molily sagt: in deiner neuen Version müssen auf jedenfall beide Bedingungen ausgewertet werden, bevor die Funktion aufgerufen werden kann. Bei Version B sollte m.W. bedingung2 nicht ausgewertet werden, wenn bedingung1 false-ish ist.

    Version A und B sollten gleich sein (zumindest von den Nebeneffekten her). Die Unterschiede liegen wohl irgendwo im Interpreter.

    Bis die Tage,
    Matti

    1. Hi,

      Unabhängig von dem, was molily sagt: in deiner neuen Version müssen auf jedenfall beide Bedingungen ausgewertet werden, bevor die Funktion aufgerufen werden kann.

      An dieser Stelle ist mein Pseudo-Code "falsch"/missverständlich. Ich übergebe Werte und führe die eigentliche Prüfung erst innerhalb der Funktion aus.

      Der Hinweis auf den Interpreter ist für mich das Entscheidende. Der Interpreter ist ein gutes Argument, sich eben nicht auf atomarer Ebene mit der Performance auseinanderzusetzen, sondern den Fokus auf die Performance der Programmlogik zu legen. Ich habe meinen Sündenbock ;-)

      Gruß Fabulit