Wann sollte ich mich für Leistung interessieren?


16

Die längste Zeit an Orten wie Javas IRC-Kanal , SO und an anderen Orten wurde mir etwas in Anlehnung an "Sorgen Sie sich darum, wie der Code aussieht und ob er jetzt lesbar / verständlich ist, und die Leistung später, falls unbedingt erforderlich" mitgeteilt. Daher war ich lange Zeit kein großer Leistungsexperte für meine kleinen Desktop- oder Web-Apps, nur um die offensichtlich ineffizienten zu entfernen.

Die meisten Antworten lauten "Was ist mit Skalierbarkeit?". Das ist ein legitimer Punkt, aber wenn meine App nur zum Parsen von Dateien mit einer Länge von beispielsweise 10.000 Zeilen erstellt wurde, sollte ich meinen Code für den kleinen Prozentsatz der Leute, die eine Datei mit 1.000.000 Zeilen erstellen, zu einem Chaos machen?

Meine Hauptfrage ist, wann ich die einfachen, aber etwas ineffizienten Methoden zum Ausführen von Aufgaben gegen große, komplizierte Bestien eintauschen soll, die zwar extrem schnell funktionieren, aber alle möglichen Methoden zum Aktualisieren zerstören und den Code für den nächsten Entwickler ohnehin übermäßig schwierig und anfällig machen?

Antworten:


23

Sorgen Sie sich um die Leistung, wenn es zu einem Problem wird.

Wenn Sie eine kleine App zur Verarbeitung von 10.000 Zeilendateien schreiben und jede 100. Datei eine 1.000.000 Zeilendatei erhalten, spielt es wahrscheinlich keine Rolle, dass die Verarbeitung dieser einen Datei länger dauert. Wenn Sie jedoch regelmäßig Dateien erhalten, die fünf bis zehn Mal so groß sind wie ursprünglich, und Ihre Anwendung zu lange braucht, um ihre Aufgabe zu erfüllen, beginnen Sie mit der Profilerstellung und Optimierung.

Jetzt sagte ich "zu lange, um seine Arbeit zu erledigen". Die Entscheidung darüber liegt beim Nutzer oder Sponsor. Wenn ich eine Aufgabe erledige und ich 5 Minuten brauche, um etwas zu erledigen, wenn ich 3 Minuten ohne die Software oder mit einem anderen Tool gebraucht habe, würde ich wahrscheinlich einen Fehlerbericht oder eine Wartungsanfrage einreichen, um dies zu verbessern.

Wenn Sie der Benutzer sind, liegt es an Ihnen, wie lange es dauern soll, bis Ihre Software ihre Arbeit erledigt hat. Nur Sie können entscheiden, ob Sie es schneller erledigen möchten oder ob Sie bereit sind, länger auf besser lesbaren Code zu warten.



Ich beginne mit der Profilerstellung und Optimierung. Wenn 1) der Job zu lange dauert 2) eine der Hardwareressourcen maximal ist (z. B. 100% CPU)
A. Binzxxxxxx

10

Meine Hauptfrage ist, wann ich die einfachen, aber etwas ineffizienten Methoden zum Ausführen von Aufgaben gegen große, komplizierte Bestien eintauschen soll, die zwar extrem schnell funktionieren, aber alle möglichen Methoden zum Aktualisieren zerstören und den Code für den nächsten Entwickler ohnehin übermäßig schwierig und anfällig machen?

Dies ist normalerweise eine falsche Zweiteilung . Sie können wunderbar effizienten, lesbaren und wartbaren Code schreiben. Sie können wundervoll ineffiziente, nicht zu wartende Unmengen an Unordnung schreiben.

Wenn ich mich mit Leistungsproblemen befasse, versuche ich normalerweise, über das Geschäftsproblem nachzudenken, das ich löse. Wie wird sich meine Software verhalten, wenn meine Kunden sie verwenden? Wird die Leistung meiner Anwendungen Jacob Nielsen glücklich machen ?


5
++ FALSCHE DICHOTOMIE! Werden sie nie lernen? Wenn Sie ein Leistungsproblem finden und beheben, ist der Code nicht nur schneller, sondern auch besser . Ich bedaure nur, dass ich nur eine positive Bewertung abzugeben habe!
Mike Dunlavey

+1 für das Schreiben, dass es normalerweise eine falsche Zweiteilung ist ... nicht immer, aber normalerweise.
Dan Rosenstark

1
-1 für das Schreiben ist es normalerweise eine falsche Zweiteilung - Tatsache ist, es ist normalerweise richtig und nur in seltenen Fällen eine falsche Zweiteilung. In mehr als 30 Jahren meiner Programmierkarriere habe ich zu viele "gut gemeinte" Leistungsoptimierungen gesehen, die das Verständnis und die Pflege des Codes erschwert haben (und oft etwas optimiert haben, dessen Optimierung völlig unnötig war).
Doc Brown

5

Eine Binsenweisheit, die ich beim Studium von Mikroprozessoren am College aufgegriffen habe und die mir erhalten geblieben ist: "Machen Sie den allgemeinen Fall schnell. Machen Sie den seltenen Fall richtig."

Solange Sie nur einen kleinen Prozentsatz von Benutzern haben, die Ihren Code mit Eingaben verschlucken, die um zwei Größenordnungen größer sind als vorgesehen, sollten Sie nicht schwitzen. Vergewissern Sie sich, dass die Eingabe korrekt verarbeitet wird, wenn sie lang genug ist, und dass nichts unbrauchbar wird, wenn der Job abgebrochen wird, bevor er beendet ist.

Aber wenn immer mehr Leute damit anfangen (oder dir sagen: "Weißt du, ich würde das Tool, das du in meinen wöchentlichen TPS-Berichten geschrieben hast, sehr gerne verwenden, aber es dauert alles verdammt lange"), dann ist es soweit Sie beginnen zu überlegen, ob Sie die Wartungsfreundlichkeit gegen Leistungszuwächse eintauschen möchten.


1

Meine Hauptfrage ist, wann ich die einfachen, aber etwas ineffizienten Methoden zum Ausführen von Aufgaben gegen große, komplizierte Bestien eintauschen soll, die zwar extrem schnell funktionieren, aber alle möglichen Methoden zum Aktualisieren zerstören und den Code für den nächsten Entwickler ohnehin übermäßig schwierig und anfällig machen?

"Sorgen Sie sich darum, wie der Code jetzt aussieht und wie er lesbar / verständlich ist, und führen Sie ihn später durch, falls dies unbedingt erforderlich ist", ist der einfache Ausweg und im Allgemeinen nicht hilfreich. Ein gutes Design ist einfach zu warten, leicht zu lesen und effizient.

Leistung ist eine gemeinsame Komponente eines guten Designs. Wenn Ihr Programm langsam und verschwenderisch ist, ist es wirklich nicht wiederverwendbar. Wenn Sie dieses Problem beheben müssen , erzwingen Sie Updates für Ihre Clients, es sei denn, es ist zu zeitaufwändig, um sie zu aktualisieren. Dieses langsame Programm wird zum großen Durcheinander, das zu kostspielig ist, um es zu verbessern. Dann wählen sie eine Alternative, da sie nicht ihren Bedürfnissen entspricht. Das Diagnostizieren, Aktualisieren und Behandeln von Nebenwirkungen von Verbesserungen an einem schlechten Design überwiegt häufig die anfängliche Entwicklungszeit des Schreibens, um effizient zu sein, korrekt zu arbeiten und ein allgemein gutes Design zu haben. Dieses Programm ist in hohem Maße wiederverwendbar und erfordert einen geringen Wartungsaufwand (Win).

Daher lautet die kurze Antwort auf Ihre Frage: "Verschwenden Sie nichts. Schreiben Sie für die Wiederverwendung. Es ist in Ordnung, faul zu sein, wenn Sie Prototypen erstellen / Proofs von Konzepten entwickeln, aber verwenden Sie diesen Prototyp nicht für Produktionscode."

Achten Sie beim Schreiben von Produktionsprogrammen und Programmen, die Sie wiederverwenden möchten, auf verschwenderische Entwürfe und vermeiden Sie diese. Während der Implementierung ist der ideale Zeitpunkt, um Ihr Programm zu schreiben, um keine Verschwendung zu verursachen. Sie haben eine klare Vorstellung von den Details und der Funktionsweise und es ist sehr schmerzhaft und ineffektiv, das Programm nach dem Schreiben zu reparieren. Viele Leute glauben, dass ein wenig Profiling (vielleicht) am Ende oder wenn es ein Problem gibt, angemessen ist, wenn es normalerweise zu zeitaufwendig ist, um es umzugestalten / zu ändern, und die Ineffizienzen so zahlreich und weit verbreitet sind, dass Sie das Programm nicht verstehen gut basierend auf den Ergebnissen eines Profils. Dieser Ansatz nimmt während der Implementierung nur wenig Zeit in Anspruch und führt (vorausgesetzt, Sie haben dies ausreichend oft getan) in der Regel zu einem Entwurf, der um ein Vielfaches schneller ist und in viel mehr Kontexten wiederverwendbar ist. nicht verschwenderisch sein, Die Auswahl guter Algorithmen, das Nachdenken über Ihre Implementierungen und die Wiederverwendung der richtigen Implementierungen sind alle Komponenten eines guten Designs. alles von welchemVerbessert die Lesbarkeit, Wartbarkeit und Wiederverwendung häufiger als es schadet.


0

Ich versuche, Code lesbar zu machen - Leistung verdammt.

Wenn sich Code als zu langsam erweist, werde ich ihn umgestalten, um ihn schneller zu machen. Normalerweise wird der Refactoring-Prozess mit vielen Kommentaren abgeschlossen, da Code in der Regel weniger lesbar ist.


0

Ähm - Niemals?

Im Ernst, Code sollte immer so geschrieben sein, dass er leicht zu verstehen und zu warten ist.

Behandeln Sie Leistungsprobleme, sobald Sie sie identifiziert haben, und optimieren Sie den Code nicht vorab, da Sie nur raten müssen, wo die Leistungsprobleme liegen.

Wenn Ihr Code so geschrieben ist, dass er klar, präzise, ​​verständlich und wartbar ist, sollten Sie oder ein anderer Programmierer kein Problem damit haben, den Code zu überarbeiten, um ihn effizienter zu gestalten.


3
Dem stimme ich nicht zu. Eine Leistungsanforderung ist eine gültige, nicht funktionsfähige Anforderung für ein System.
Thomas Owens

Technisch gesehen kann man sagen, wenn es eine klar definierte leistungsbezogene Anforderung gibt, dass Sie ein Leistungsproblem identifiziert haben und es in Ihrer Lösung berücksichtigen müssen. Ich spreche davon, im Voraus klug zu werden, damit Sie unspezifische "potenzielle" Probleme vermeiden können.
Noah Goodrich

Ah. Ja, in diesem Fall hast du absolut recht. Sie sorgen sich nicht um die Möglichkeiten, weil es so viele gibt, sondern konzentrieren sich auf das, was Sie wissen.
Thomas Owens

0

Normalerweise schreibe ich Code, um in erster Linie lesbar zu sein. Wenn und nur wenn ich finde, dass das Programm zu langsam läuft , um seine Arbeit zu erledigen, profiliere und optimiere ich es. Das heißt, es ist nichts Falsches daran, sich daran zu gewöhnen, allgemeine Optimierungen durchzuführen, die die Lesbarkeit Ihres Codes nicht beeinträchtigen. Das heißt, wenn ein Codeteil auf zwei gleiche (oder nahezu gleiche) lesbare Arten geschrieben werden kann, wählen Sie diejenige, die normalerweise schneller ist.

In Python sind beispielsweise Listenverständnisse (oder Generatorausdrücke) in der Regel schneller als die entsprechende forSchleife. Daher verwende ich Listenverständnisse, wenn sie die Lesbarkeit nicht beeinträchtigen (z. B. verschachtele ich Listenverständnisse nicht, wenn Ich kann dies vermeiden und stattdessen eine for-Schleife verwenden, da es schwierig sein kann, verschachtelte Listenverständnisse mental zu analysieren.

In ähnlicher Weise sind unveränderliche Datentypen in der Regel schneller als veränderbare. Daher verwende ich unveränderliche Datentypen, wo ich kann.


0

Wenn Sie in wirklich leistungskritischen Bereichen arbeiten, können Sie die Effizienz nicht nachträglich beeinträchtigen. In solchen Fällen und in Bezug auf die Wartbarkeit des Endergebnisses ist dies eine der wichtigsten Überlegungen, die bei der frühzeitigen Konzeption zu berücksichtigen sind.

Sie können keinen großen Server entwerfen und implementieren und einfach damit beginnen, einfachen, gut dokumentierten Code zu schreiben, der nur Blockierungsfunktionen für alle Elemente mit einer globalen Thread-Sperre verwendet, die das gesamte System sperrt, um jede einzelne Client-Anfrage zu verarbeiten, ohne eine zu platzieren dachte was auch immer in geteilten Zustand, Fadenkonflikt und Asynchronität. Dies ist ein Rezept für eine Katastrophe und das Erfordernis, den Großteil des gut dokumentierten Codes, den Sie geschrieben haben, so umzugestalten und neu zu schreiben, dass er zu einer Codebasis führt, die nur schwer zu warten ist im nachhinein die erforderliche Effizienz zu erreichen, anstatt im Vorfeld über effiziente, einfache und funktionierende Designs nachgedacht zu haben.

Ein 8-monatiges Game-Entwicklungsteam mit einer Engine, die mit 32 Kernen nur 2 Frames pro Sekunde auf der leistungsstärksten Hardware laufen lässt und dazu neigt, jedes Mal, wenn der Bildschirm voll ist, für 15 Sekunden stehen zu bleiben, wird wahrscheinlich nicht sofort ein brauchbares Produkt erhalten Behebung eines kleinen lokalisierten Hotspots. Wahrscheinlich ist ihr Design FUBAR in einer Weise, die eine epische Überarbeitung des Zeichenbretts und Designänderungen, die in jede Ecke der Codebasis kaskadieren könnten, rechtfertigt.

Mit John Carmack sprach er einmal darüber, wie eine Tech-Demo mit mindestens Hunderten bis Tausenden von Bildern pro Sekunde ausgeführt werden muss, um sie in die Produktion zu integrieren. Das ist keine ungesunde Besessenheit von Effizienz. Er weiß im Voraus, dass Spiele in ihrer Gesamtheit über 30 FPS laufen müssen, damit die Kunden sie für akzeptabel halten. Infolgedessen kann ein kleiner Aspekt wie ein Soft Shadow-System nicht mit 30 FPS ausgeführt werden, oder das Spiel als Ganzes kann möglicherweise nicht schnell genug sein, um das erforderliche Echtzeit-Feedback zu liefern. Es ist unbrauchbar, bis es die erforderliche Effizienz erreicht. In solchen leistungskritischen Bereichen, in denen grundlegende Anforderungen an die Effizienz gestellt werden, ist eine Lösung, die keine ausreichende Geschwindigkeit erreicht, nicht besser als eine, die überhaupt nicht funktioniert.. Und Sie können kein effizientes Soft-Shadow-System entwerfen, das mit Hunderten bis Tausenden von Frames pro Sekunde läuft, wie es für eine Echtzeit-Game-Engine erforderlich ist, es sei denn, Sie haben vorher über die Effizienz nachgedacht. Tatsächlich sind in solchen Fällen 90% der Arbeit auf Effizienz ausgerichtet, da es trivial ist, ein weiches Schattensystem zu entwickeln, das bei Verwendung der Pfadverfolgung bei 2 Stunden pro Frame einwandfrei funktioniert. Sie können jedoch nicht damit rechnen, es zu optimieren mit Hunderten von Bildern pro Sekunde ohne eine völlig andere Änderung in der Herangehensweise zu laufen.

Wenn Effizienz ein wesentlicher Bestandteil des Entwurfs einer Anwendung ist, können Sie im Nachhinein keine Effizienz erwarten, ohne wesentlich mehr Zeit zu verlieren, als Sie durch Ignorieren gespart haben, da Sie im Nachhinein keine funktionierende Konstruktion erwarten können. Niemand sagt: "Es ist in Ordnung, sich erst später Gedanken über das Design zu machen. Dokumentieren Sie einfach Ihren Code gut und Sie können sich später ein passendes Design einfallen lassen ." In leistungskritischen Architekturen ist dies jedoch genau das, was Sie effektiv tun, wenn Sie im Vorfeld nicht viel Sorgfalt und Bedacht auf effiziente Entwürfe verwenden.

Das bedeutet nicht, dass Sie Ihre Implementierungen von Anfang an optimieren müssen. Für Implementierungsdetails gibt es viel Spielraum, um nach der Messung schnellere Lösungen zu finden, vorausgesetzt, das Design muss nicht geändert werden. Oft ist dies die produktivste Methode. Auf der Designebene bedeutet dies jedoch, dass Sie von Anfang an ausreichend darüber nachdenken müssen, wie sich Design und Architektur auf die Effizienz auswirken.

Der entscheidende Unterschied ist hier das Design. Es ist im Nachhinein nicht einfach, große Änderungen an Konstruktionen vorzunehmen, da sich Abhängigkeiten bei Konstruktionen ansammeln und die Abhängigkeiten sich auflösen, wenn sich die Konstruktion ändert. Und wenn für ein Design die Anforderung besteht, dass es einigermaßen effizient ist oder in einigen Fällen, dass seine Qualität weitgehend an seiner Effizienz gemessen wird, sollten Sie nicht erwarten, dass Sie im Nachhinein in der Lage sind, ein ordnungsgemäßes Design zu erzielen. Bei allen Wettbewerbsprodukten, bei denen Effizienz ein wichtiger Qualitätsaspekt ist, egal ob es sich um Betriebssysteme, Compiler, Videoprozessoren, Raytracer, Game Engines oder Physics Engines handelt, wurde von Anfang an akribisch über Effizienzgedanken und Datenrepräsentationen nachgedacht. Und in diesen Fällen ist es keine vorzeitige Optimierung, sich vorab so viele Gedanken über die Effizienz zu machen. Es war genau an der produktivsten Zeit, solche Gedanken zu platzieren, um es zu tun,

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.