Ein Kollege von mir hat heute eine Klasse namens geschrieben ThreadLocalFormat
, die im Grunde genommen Instanzen von Java-Format-Klassen in ein Thread-Local verschoben hat, da sie nicht thread-sicher und "relativ teuer" zu erstellen sind. Ich schrieb einen Schnelltest und errechnete, dass ich 200.000 Instanzen pro Sekunde erstellen könnte, und fragte ihn, ob er so viele erstellen könne, auf die er antwortete, "bei weitem nicht so viele". Er ist ein großartiger Programmierer und jeder im Team ist hoch qualifiziert, sodass wir den resultierenden Code problemlos verstehen können. Es handelte sich jedoch eindeutig um einen Optimierungsfall, bei dem kein wirklicher Bedarf besteht. Auf meine Bitte hin hat er den Code zurückgesetzt. Was denkst du? Handelt es sich um "vorzeitige Optimierung" und wie schlimm ist es wirklich?
quelle
Antworten:
Beachten Sie unbedingt das vollständige Zitat:
Dies bedeutet, dass Sie mangels gemessener Leistungsprobleme nicht optimieren sollten, da Sie glauben, dass Sie einen Leistungsgewinn erzielen werden. Es gibt offensichtliche Optimierungen (z. B. keine Verkettung von Zeichenfolgen in einer engen Schleife), aber alles, was nicht trivial klar ist, sollte vermieden werden, bis es gemessen werden kann.
Das größte Problem bei der "vorzeitigen Optimierung" besteht darin, dass sie unerwartete Fehler verursachen und viel Zeit verschwenden kann.
quelle
HashSet
anstelle von a eineList
vorzeitige Optimierung war. Der fragliche Anwendungsfall war eine statisch initialisierte Sammlung, deren einziger Zweck darin bestand, als Nachschlagetabelle zu dienen. Ich glaube nicht, dass ich mich irre, wenn ich sage, dass es einen Unterschied zwischen der Auswahl des richtigen Tools für den Job und der vorzeitigen Optimierung gibt. Ich denke, Ihr Beitrag bestätigt diese Philosophie:There are obvious optimizations...anything that isn't trivially clear optimization should be avoided until it can be measured.
Die Optimierung eines HashSets wurde sorgfältig gemessen und dokumentiert.Set
ist auch semantisch korrekter und informativer alsList
, es geht also um mehr als den Optimierungsaspekt.Vorzeitige Mikrooptimierungen sind die Wurzel allen Übels, weil Mikrooptimierungen den Kontext auslassen. Sie verhalten sich fast nie so, wie sie es erwarten.
Was sind einige gute frühe Optimierungen in der Reihenfolge der Wichtigkeit:
Einige Optimierungen für den mittleren Entwicklungszyklus:
Einige Optimierungen am Ende des Entwicklungszyklus
Nicht alle frühen Optimierungen sind böse, Mikrooptimierungen sind böse, wenn sie zum falschen Zeitpunkt im Entwicklungslebenszyklus durchgeführt werden , da sie die Architektur beeinträchtigen, die anfängliche Produktivität beeinträchtigen, in Bezug auf die Leistung irrelevant sein oder sich am Ende sogar nachteilig auswirken können der Entwicklung aufgrund unterschiedlicher Umgebungsbedingungen.
Wenn Leistung von Belang ist (und immer sein sollte), denken Sie immer groß . Leistung ist ein größeres Bild und nicht etwa: Soll ich int oder long verwenden ? Wählen Sie Top-Down, wenn Sie mit Leistung anstelle von Bottom-Up arbeiten .
quelle
Optimierung ohne Erstmessung ist fast immer verfrüht.
Ich glaube, das stimmt in diesem Fall und auch im allgemeinen Fall.
quelle
Optimierung ist "böse", wenn sie verursacht:
In Ihrem Fall scheint es, als wäre bereits ein wenig Zeit für Programmierer aufgewendet worden, der Code war nicht zu komplex (eine Vermutung aus Ihrem Kommentar, die jeder im Team verstehen könnte), und der Code ist ein bisschen zukunftssicherer (seiend) thread safe jetzt, wenn ich deine beschreibung verstanden habe). Klingt nur ein bisschen böse. :)
quelle
Ich bin überrascht, dass diese Frage 5 Jahre alt ist, und dennoch hat niemand mehr von Knuth geschrieben, als ein paar Sätze. Die paar Absätze, die das berühmte Zitat umgeben, erklären es ziemlich gut. Das Papier, das zitiert wird, heißt " Structured Programming with go to Statements " und handelt, obwohl es fast 40 Jahre alt ist, von einer Kontroverse und einer Softwarebewegung, die beide nicht mehr existieren, und enthält Beispiele in Programmiersprachen, die viele Menschen noch nie gesehen haben Davon gehört, dass eine überraschend große Menge von dem, was gesagt wurde, immer noch zutrifft.
Hier ist ein größeres Zitat (ab Seite 8 des PDF, Seite 268 im Original):
Ein weiteres gutes Stück von der vorherigen Seite:
quelle
Ich habe oft gesehen, dass dieses Zitat verwendet wurde, um offensichtlich schlechten Code oder Code zu rechtfertigen, der, obwohl seine Leistung nicht gemessen wurde, wahrscheinlich recht einfach schneller gemacht werden könnte, ohne die Codegröße zu erhöhen oder seine Lesbarkeit zu beeinträchtigen.
Im Allgemeinen halte ich frühe Mikrooptimierungen für eine schlechte Idee. Makrooptimierungen (z. B. die Auswahl eines O (log N) -Algorithmus anstelle von O (N ^ 2)) sind jedoch häufig sinnvoll und sollten frühzeitig durchgeführt werden, da es möglicherweise verschwenderisch ist, einen O (N ^ 2) -Algorithmus zu schreiben und Werfen Sie es dann vollständig weg, und verwenden Sie die Methode O (log N).
Beachten Sie, dass die Wörter sein können : Wenn der O (N ^ 2) -Algorithmus einfach und leicht zu schreiben ist, können Sie ihn später ohne viel Schuldgefühl wegwerfen, wenn er sich als zu langsam herausstellt. Wenn jedoch beide Algorithmen ähnlich komplex sind oder wenn die erwartete Arbeitslast so hoch ist, dass Sie bereits wissen, dass Sie die schnellere benötigen, ist eine frühzeitige Optimierung eine fundierte technische Entscheidung, die Ihre gesamte Arbeitslast auf lange Sicht reduziert.
Daher denke ich im Allgemeinen, dass der richtige Ansatz darin besteht, herauszufinden, welche Optionen Sie haben, bevor Sie mit dem Schreiben von Code beginnen, und den besten Algorithmus für Ihre Situation zu wählen. Am wichtigsten ist jedoch, dass der Ausdruck "vorzeitige Optimierung ist die Wurzel allen Übels" keine Entschuldigung für Unwissenheit ist. Karriereentwickler sollten eine allgemeine Vorstellung davon haben, wie hoch die allgemeinen Betriebskosten sind. sie sollten zum Beispiel wissen,
Entwickler sollten mit einer Toolbox von Datenstrukturen und Algorithmen vertraut sein, damit sie problemlos die richtigen Tools für den Job verwenden können.
Mit viel Wissen und einer persönlichen Toolbox können Sie nahezu mühelos optimieren. Es ist böse, viel Mühe in eine Optimierung zu stecken, die möglicherweise unnötig ist (und ich gebe zu, mehr als einmal in diese Falle zu tappen). Aber wenn die Optimierung so einfach ist wie das Auswählen einer Menge / einer Hashtabelle anstelle eines Arrays oder das Speichern einer Liste von Zahlen in double [] anstelle von string [], warum dann nicht? Ich bin mir nicht sicher, ob ich hier mit Knuth nicht einverstanden bin, aber ich glaube, er hat von Low-Level-Optimierung gesprochen, während ich von High-Level-Optimierung spreche.
Denken Sie daran, dass dieses Zitat ursprünglich aus dem Jahr 1974 stammt. Im Jahr 1974 waren Computer langsam und die Rechenleistung teuer, was einigen Entwicklern die Tendenz gab, Zeile für Zeile zu optimieren. Ich glaube, Knuth hat sich dagegen gewehrt. Er sagte nicht "mach dir überhaupt keine Sorgen um die Leistung", denn 1974 wäre das nur verrücktes Gerede. Knuth erklärte, wie man optimiert; Kurz gesagt, man sollte sich nur auf die Engpässe konzentrieren, und bevor Sie dies tun, müssen Sie Messungen durchführen, um die Engpässe zu finden.
Beachten Sie, dass Sie die Engpässe erst finden können, wenn Sie ein zu messendes Programm geschrieben haben. Dies bedeutet, dass einige Leistungsentscheidungen getroffen werden müssen , bevor etwas zu messen ist. Manchmal ist es schwierig, diese Entscheidungen zu ändern, wenn Sie sie falsch verstehen. Aus diesem Grund ist es gut, eine allgemeine Vorstellung davon zu haben, was Dinge kosten, damit Sie vernünftige Entscheidungen treffen können, wenn keine festen Daten verfügbar sind.
Wie früh die Optimierung beginnt und inwieweit Sie sich um die Leistung sorgen müssen, hängt vom jeweiligen Job ab. Wenn Sie Skripte schreiben, die Sie nur ein paar Mal ausführen, ist es in der Regel reine Zeitverschwendung, sich Gedanken über die Leistung zu machen. Wenn Sie jedoch für Microsoft oder Oracle arbeiten und an einer Bibliothek arbeiten, die Tausende anderer Entwickler auf tausende verschiedene Arten verwenden werden, lohnt es sich möglicherweise, sie zu optimieren, damit Sie die gesamte Bandbreite abdecken können Use Cases effizient nutzen. Trotzdem muss das Leistungsbedürfnis immer gegen das Bedürfnis nach Lesbarkeit, Wartbarkeit, Eleganz, Erweiterbarkeit usw. abgewogen werden.
quelle
Persönlich in einem so bedeckt vorherigen Thread , ich glaube nicht , dass frühe Optimierung ist schlecht in Situationen , in denen Sie wissen , dass Sie Performance - Probleme getroffen. Zum Beispiel schreibe ich Oberflächenmodellierungs- und Analysesoftware, in der ich mich regelmäßig mit zig Millionen von Entitäten befasse. Das Planen einer optimalen Leistung in der Entwurfsphase ist der späten Optimierung eines schwachen Entwurfs weit überlegen.
Eine weitere zu berücksichtigende Frage ist, wie sich Ihre Anwendung in Zukunft skalieren lässt. Wenn Sie davon ausgehen, dass Ihr Code eine lange Lebensdauer hat, ist die Optimierung der Leistung in der Entwurfsphase ebenfalls eine gute Idee.
Nach meiner Erfahrung bietet die späte Optimierung magere Belohnungen zu einem hohen Preis. Die Optimierung in der Entwurfsphase durch Algorithmusauswahl und Optimierung ist viel besser. Abhängig davon, dass ein Profiler versteht, wie Ihr Code funktioniert, ist dies kein guter Weg, um leistungsstarken Code zu erhalten. Sie sollten dies im Voraus wissen.
quelle
Tatsächlich habe ich gelernt, dass vorzeitige Nichtoptimierung häufiger die Wurzel allen Übels ist.
Beim Schreiben von Software treten zunächst Probleme auf, wie Instabilität, eingeschränkte Funktionen, schlechte Bedienbarkeit und schlechte Leistung. All dies wird normalerweise behoben, wenn die Software ausgereift ist.
Alles außer Leistung. Niemand scheint sich um Leistung zu kümmern. Der Grund ist einfach: Wenn eine Software abstürzt, wird jemand den Fehler beheben und das ist es, wenn ein Feature fehlt, wird jemand es implementieren und fertig, wenn die Software eine schlechte Leistung hat, liegt es in vielen Fällen nicht an einer fehlenden Mikrooptimierung, sondern daran wegen schlechten Designs und niemand wird das Design der Software berühren. JE.
Schau dir Bochs an. Es ist langsam wie die Hölle. Wird es jemals schneller werden? Vielleicht, aber nur im Bereich einiger Prozent. Es wird niemals eine Leistung erzielen, die mit Virtualisierungssoftware wie VMWare oder VBox oder sogar QEMU vergleichbar ist. Weil es von Natur aus langsam ist!
Wenn das Problem einer Software darin besteht, dass sie langsam ist, dann weil sie SEHR langsam ist und dies nur durch eine Verbesserung der Leistung um eine Vielzahl von Faktoren behoben werden kann. + 10% machen eine langsame Software einfach nicht schnell. Und bei späteren Optimierungen werden Sie in der Regel nicht mehr als 10% erhalten.
Wenn also die Leistung für Ihre Software wichtig ist, sollten Sie dies von Anfang an bei der Entwicklung berücksichtigen, anstatt zu denken: "Oh ja, es ist langsam, aber das können wir später verbessern." Weil du nicht kannst!
Ich weiß, dass das nicht wirklich zu Ihrem speziellen Fall passt, aber es beantwortet die allgemeine Frage "Ist vorzeitige Optimierung wirklich die Wurzel allen Übels?" - mit einem klaren NEIN.
Jede Optimierung, wie jede Funktion usw. muss sorgfältig entworfen und sorgfältig implementiert werden. Dazu gehört auch eine angemessene Bewertung von Kosten und Nutzen. Optimieren Sie einen Algorithmus nicht, um hier und da einige Zyklen zu sparen, wenn dadurch kein messbarer Leistungsgewinn erzielt wird.
Nur als Beispiel: Sie können die Leistung einer Funktion verbessern, indem Sie sie inline setzen und möglicherweise eine Handvoll Zyklen einsparen. Gleichzeitig erhöhen Sie wahrscheinlich die Größe Ihrer ausführbaren Datei und erhöhen die Wahrscheinlichkeit von TLB- und Cache-Ausfällen, die Tausende von Zyklen oder sogar Kosten verursachen Paging-Vorgänge, die die Leistung vollständig beeinträchtigen. Wenn Sie diese Dinge nicht verstehen, kann Ihre "Optimierung" schlecht ausfallen.
Dumme Optimierung ist schlimmer als "vorzeitige" Optimierung, aber beide sind immer noch besser als vorzeitige Nichtoptimierung.
quelle
Es gibt zwei Probleme mit PO: Erstens, die Entwicklungszeit, die für nicht wesentliche Arbeiten verwendet wird, die zum Schreiben weiterer Funktionen oder zum Beheben weiterer Fehler verwendet werden könnten, und zweitens, das falsche Sicherheitsgefühl, dass der Code effizient ausgeführt wird. PO beinhaltet oft die Optimierung von Code, der nicht zum Flaschenhals wird, während der Code, der dazu führt, nicht bemerkt wird. Das "vorzeitige" Bit bedeutet, dass die Optimierung durchgeführt wird, bevor ein Problem unter Verwendung geeigneter Messungen identifiziert wird.
Im Grunde klingt das nach vorzeitiger Optimierung, aber ich würde es nicht unbedingt zurücksetzen, wenn es keine Bugs einführt - schließlich wurde es jetzt optimiert (!)
quelle
Ich glaube, Mike Cohn nennt das "Vergolden" des Codes - das heißt, er verbringt Zeit mit Dingen, die nett sein könnten, aber nicht notwendig sind.
Er riet davon ab.
PS: "Vergolden" könnte in Bezug auf die Funktionalität ein Kinderspiel sein. Wenn Sie sich den Code ansehen, handelt es sich um unnötige Optimierungen, zukunftssichere Klassen usw.
quelle
Da es kein Problem gibt, den Code zu verstehen, kann dieser Fall als Ausnahme angesehen werden.
Im Allgemeinen führt die Optimierung jedoch zu weniger lesbarem und verständlichem Code und sollte nur bei Bedarf angewendet werden. Ein einfaches Beispiel: Wenn Sie wissen, dass Sie nur ein paar Elemente sortieren müssen, verwenden Sie BubbleSort. Wenn Sie jedoch den Verdacht haben, dass die Elemente zunehmen könnten und Sie nicht wissen, wie viel es ist, ist die Optimierung mit QuickSort (zum Beispiel) nicht schlecht, sondern ein Muss. Und dies sollte bei der Gestaltung des Programms berücksichtigt werden.
quelle
Ich habe festgestellt, dass das Problem mit der vorzeitigen Optimierung meistens auftritt, wenn vorhandener Code neu geschrieben wird, um schneller zu sein. Ich kann sehen, dass es ein Problem sein könnte, zunächst eine verschlungene Optimierung zu schreiben, aber meistens sehe ich eine verfrühte Optimierung, die ihren hässlichen Kopf in der Korrektur dessen aufrichtet, was nicht (bekanntermaßen) kaputt ist.
Und das schlimmste Beispiel dafür ist, wenn ich jemanden sehe, der Features aus einer Standardbibliothek erneut implementiert. Das ist eine große rote Fahne. Ich habe einmal gesehen, wie jemand benutzerdefinierte Routinen für die Manipulation von Zeichenfolgen implementierte, weil er befürchtete, die eingebauten Befehle seien zu langsam.
Dies führt dazu, dass Code schwerer zu verstehen ist (schlecht) und viel Zeit für die Arbeit benötigt wird, was wahrscheinlich nicht nützlich ist (schlecht).
quelle
Aus einer anderen Perspektive ist es meine Erfahrung, dass die meisten Programmierer / Entwickler keinen Erfolg planen und der "Prototyp" fast immer Release 1.0 wird. Ich habe Erfahrung aus erster Hand mit 4 verschiedenen Originalprodukten, bei denen das elegante, sexy und hochfunktionale Front-End (im Grunde die Benutzeroberfläche) zu einer weit verbreiteten Akzeptanz und Begeisterung der Benutzer geführt hat. Bei jedem dieser Produkte schlichen sich Leistungsprobleme innerhalb relativ kurzer Zeit (1 bis 2 Jahre) ein, insbesondere als größere, anspruchsvollere Kunden begannen, das Produkt zu übernehmen. Sehr bald dominierte die Leistung die Themenliste, obwohl die Entwicklung neuer Funktionen die Prioritätenliste des Managements dominierte. Die Kunden wurden immer frustrierter, als jede Veröffentlichung neue Funktionen hinzufügte, die sich gut anhörten, aber aufgrund von Leistungsproblemen fast nicht zugänglich waren.
Sehr grundlegende Design- und Implementierungsfehler, die beim "Prototyp" keine oder nur geringe Bedeutung hatten, wurden zu großen Hindernissen für den langfristigen Erfolg der Produkte (und der Unternehmen).
Ihre Kundendemo kann mit XML-DOMs, SQL Express und vielen clientseitig zwischengespeicherten Daten auf Ihrem Laptop großartig aussehen und Leistung bringen. Wenn Sie erfolgreich sind, stürzt das Produktionssystem wahrscheinlich einen Brennvorgang ab.
1976 diskutierten wir immer noch über die optimalen Methoden zur Berechnung einer Quadratwurzel oder zum Sortieren eines großen Arrays, und Don Knuths Sprichwort richtete sich auf den Fehler, sich darauf zu konzentrieren, diese Art von Routine auf niedriger Ebene zu Beginn des Entwurfsprozesses zu optimieren, anstatt sich auf die Lösung des Problems zu konzentrieren und dann die lokalisierten Codebereiche zu optimieren.
Wenn man das Sprichwort wiederholt, um zu entschuldigen, dass kein effizienter Code (C ++, VB, T-SQL oder andere) geschrieben wurde, oder um den Datenspeicher nicht richtig zu entwerfen oder die Netzwerkarchitektur nicht zu berücksichtigen, dann demonstrieren sie IMO nur a sehr flaches Verständnis für die wahre Natur unserer Arbeit. Strahl
quelle
Ich nehme an, es hängt davon ab, wie Sie "vorzeitig" definieren. Es ist nicht von Natur aus böse, die Funktionalität auf niedriger Ebene beim Schreiben schnell zu machen. Ich denke, das ist ein Missverständnis des Zitats. Manchmal denke ich, dass dieses Zitat mehr Qualifikation verträgt. Ich würde jedoch die Kommentare von m_pGladiator zur Lesbarkeit wiederholen.
quelle
Die Antwort lautet: es kommt darauf an. Ich werde argumentieren, dass Effizienz für bestimmte Arten von Arbeiten, wie komplexe Datenbankabfragen, eine große Rolle spielt. In vielen anderen Fällen verbringt der Computer die meiste Zeit damit, auf Benutzereingaben zu warten. Daher ist die Optimierung des meisten Codes im besten Fall eine Verschwendung von Aufwand und im schlimmsten Fall kontraproduktiv.
In einigen Fällen können Sie das Design auf Effizienz oder Leistung (wahrgenommen oder real) ausrichten - indem Sie einen geeigneten Algorithmus auswählen oder eine Benutzeroberfläche entwerfen, damit bestimmte kostspielige Vorgänge beispielsweise im Hintergrund ausgeführt werden. In vielen Fällen erhalten Sie durch Profilerstellung oder andere Vorgänge zum Ermitteln von Hotspots einen Vorteil von 10/90.
Ein Beispiel dafür, das ich beschreiben kann, ist das Datenmodell, das ich einmal für ein Gerichtsverwaltungssystem durchgeführt habe, das ungefähr 560 Tabellen enthielt. Es begann normalisiert ("wunderschön normalisiert", wie es der Berater einer bestimmten Big-5-Firma ausdrückte), und wir mussten nur vier denormalisierte Daten darin ablegen:
Eine materialisierte Ansicht zur Unterstützung eines Suchbildschirms
Eine vom Trigger verwaltete Tabelle zur Unterstützung eines anderen Suchbildschirms, der mit einer materialisierten Ansicht nicht möglich ist.
Eine denormalisierte Berichtstabelle (diese gab es nur, weil wir einige Durchsatzberichte erstellen mussten, als ein Data-Warehouse-Projekt gespeichert wurde.)
Eine vom Trigger gepflegte Tabelle für eine Schnittstelle, die nach dem neuesten von ziemlich vielen unterschiedlichen Ereignissen innerhalb des Systems suchen musste.
Dies war (zu der Zeit) das größte J2EE-Projekt in Australasien - weit über 100 Jahre Entwicklerzeit - und es enthielt 4 denormalisierte Elemente im Datenbankschema, von denen eines überhaupt nicht dorthin gehörte.
quelle
Vorzeitige Optimierung ist nicht die Wurzel ALLEN Übels, das ist sicher. Es gibt jedoch Nachteile:
Anstelle einer vorzeitigen Optimierung könnte man frühzeitig Sichtbarkeitstests durchführen, um festzustellen, ob tatsächlich eine bessere Optimierung erforderlich ist.
quelle
Die meisten Befragten, die sich an "PMO" (das teilweise Anführungszeichen) halten, sagen, dass Optimierungen auf Messungen basieren müssen und dass Messungen erst ganz am Ende durchgeführt werden können.
Es ist auch meine Erfahrung aus der Entwicklung großer Systeme, dass Leistungstests am Ende durchgeführt werden, wenn die Entwicklung kurz vor dem Abschluss steht.
Wenn wir den "Ratschlägen" dieser Leute folgen würden, wären alle Systeme unerträglich langsam. Sie wären ebenfalls teuer, da ihr Hardwarebedarf viel höher ist als ursprünglich vorgesehen.
Ich habe lange empfohlen, Leistungstests in regelmäßigen Abständen während des Entwicklungsprozesses durchzuführen: Es wird sowohl das Vorhandensein von neuem Code (wo zuvor kein Code vorhanden war) als auch der Status des vorhandenen Codes angezeigt.
Eine andere Idee ist es, Software auf der Ebene der Funktionsblöcke zu instrumentieren. Während der Ausführung sammelt das System Informationen zu den Ausführungszeiten der Funktionsblöcke. Wenn ein System-Upgrade durchgeführt wird, kann festgestellt werden, welche Funktionsblöcke wie in der früheren Version ausgeführt wurden und welche sich verschlechtert haben. Auf dem Bildschirm einer Software kann über das Hilfemenü auf die Leistungsdaten zugegriffen werden.
Schauen Sie sich dieses hervorragende Stück an, was PMO bedeuten könnte oder nicht.
quelle