Wann ist die Optimierung verfrüht?


81

Wie Knuth sagte,

Wir sollten kleine Wirkungsgrade vergessen, etwa in 97% der Fälle: Vorzeitige Optimierung ist die Wurzel allen Übels.

Dies ist etwas, was häufig in Stack Overflow-Antworten auf Fragen wie "Welcher ist der effizienteste Schleifenmechanismus", "SQL-Optimierungstechniken?" ( und so weiter ). Die Standardantwort auf diese Fragen zu Optimierungstipps besteht darin, Ihren Code zu profilieren und zu prüfen, ob es sich zuerst um ein Problem handelt. Wenn dies nicht der Fall ist, ist Ihre neue Technik nicht erforderlich.

Meine Frage ist, ob eine bestimmte Technik anders, aber nicht besonders dunkel oder verschleiert ist, kann dies wirklich als vorzeitige Optimierung angesehen werden?

Hier ist ein verwandter Artikel von Randall Hyde mit dem Titel The Fallacy of Premature Optimization .


41
Es ist irgendwie ironisch, dass viele Leute, die "Vorzeitige Optimierung ist die Wurzel allen Übels" schreien, das Zitat vorzeitig optimiert haben: (Fortsetzung)
einige

21
"Wir sollten kleine Wirkungsgrade vergessen, etwa in 97% der Fälle: Vorzeitige Optimierung ist die Wurzel allen Übels. Dennoch sollten wir unsere Chancen bei diesen kritischen 3% nicht verpassen" (Donald Knuth)
etwa

2
Ich glaube, es war CA Hoare, der das gesagt hat. Sogar Knuth sagt es.
Jamesh

1
Ja, Tony Hoare sagte zuerst, die "vorzeitige Optimierung ist die Wurzel allen bösen Teils", aber Knuth zitierte / paraphrasierte ihn und fügte den Rest hinzu, glaube ich
nickf

2
Obwohl ich der Meinung bin, dass das Zitat die Frage ist, die am häufigsten missbraucht und aus dem Zusammenhang gerissen wird, ist sie per Definition aufgrund der "verfrühten" immer korrekt (sie wird jedoch meistens fälschlicherweise als Rechtfertigung für schlampiges Design und Code verwendet). Wenn die Optimierung zum günstigsten Zeitpunkt in der Entwicklung erfolgte, sei es während des Entwurfs oder an einem anderen Punkt, war sie per Definition nicht "verfrüht".
Lawrence Dol

Antworten:


103

Don Knuth startete die literarische Programmierbewegung , weil er glaubte, dass die wichtigste Funktion des Computercodes darin besteht, die Absicht des Programmierers einem menschlichen Leser mitzuteilen . Jede Codierungspraxis, die es schwieriger macht, Ihren Code im Namen der Leistung zu verstehen, ist eine vorzeitige Optimierung.

Bestimmte Redewendungen, die im Namen der Optimierung eingeführt wurden, sind so populär geworden, dass jeder sie versteht und erwartet und nicht verfrüht wird. Beispiele beinhalten

  • Verwenden der Zeigerarithmetik anstelle der Array-Notation in C, einschließlich der Verwendung von Redewendungen wie

    for (p = q; p < lim; p++)
    
  • Globale Variablen an lokale Variablen in Lua neu binden, wie in

    local table, io, string, math
        = table, io, string, math
    

Nehmen Sie über solche Redewendungen hinaus Abkürzungen auf eigene Gefahr .

Alle Optimierungen sind verfrüht, es sei denn

  • Ein Programm ist zu langsam (viele Leute vergessen diesen Teil).

  • Sie haben eine Messung (Profil oder ähnliches), die zeigt, dass die Optimierung die Dinge verbessern könnte .

(Es ist auch zulässig, den Speicher zu optimieren.)

Direkte Antwort auf Frage:

  • Wenn Ihre "andere" Technik das Programm schwerer verständlich macht , handelt es sich um eine vorzeitige Optimierung .

BEARBEITEN : Als Reaktion auf Kommentare ist die Verwendung von Quicksort anstelle eines einfacheren Algorithmus wie der Einfügesortierung ein weiteres Beispiel für eine Redewendung, die jeder versteht und erwartet . (Wenn Sie jedoch Ihre eigene Sortierroutine schreiben, anstatt die Bibliothekssortierroutine zu verwenden, hoffen Sie, dass Sie einen sehr guten Grund haben.)


13
Nach Ihren Definitionen; Wenn eine Quicksort-Implementierung schwerer zu lesen und zu verstehen ist als eine Bubblesort-Implementierung, handelt es sich um eine vorzeitige Optimierung. Sie können nicht für den Speicher optimieren? Versuchen Sie, dieselben Beispiele für große, spärliche Matrizen nachzuschlagen. Meiner Meinung nach sollten die meisten Optimierungen in der Entwurfsphase erfolgen. Ich, e, sehr früh.
SmacL

1
@frankodwyer: Das Inkrementieren des Zeigers ist jedoch möglicherweise schneller als das Inkrementieren eines Zählers und die Verwendung der Array-Notation. Dies wäre eine vorzeitige Optimierung.
Joachim Sauer

5
@Norman: Obwohl Quicksort mittlerweile allgegenwärtig ist, war es nicht die erste Erfindung , und war QED daher eine vorzeitige Optimierung, mit der der Autor nichts zu tun hatte, oder?
Lawrence Dol

5
@ Software Monkey: Auf jeden Fall. Jede CS-Forschung ist eine Verschwendung des Geldes der Steuerzahler und sollte sofort eingestellt werden.
Norman Ramsey

11
Jeder Sortieralgorithmus, einschließlich des von Ihnen erfundenen, ist klar und präzise, ​​wenn er als separate Funktion namens sortQuickly (...) mit den richtigen Kommentaren geschrieben wird.
ilya n.

40

Meiner Meinung nach sollten 90% Ihrer Optimierung in der Entwurfsphase erfolgen, basierend auf den wahrgenommenen aktuellen und vor allem zukünftigen Anforderungen. Wenn Sie einen Profiler herausnehmen müssen, weil Ihre Anwendung nicht auf die erforderliche Last skaliert werden kann, haben Sie ihn zu spät verlassen, und IMO wird viel Zeit und Mühe verschwenden, während das Problem nicht behoben werden kann.

In der Regel sind nur Optimierungen sinnvoll, mit denen Sie eine Leistungsverbesserung um eine Größenordnung in Bezug auf die Geschwindigkeit oder einen Multiplikator in Bezug auf Speicher oder Bandbreite erzielen. Diese Arten von Optimierungen beziehen sich normalerweise auf die Auswahl des Algorithmus und die Speicherstrategie und sind äußerst schwierig in vorhandenen Code umzukehren. Sie können so tief gehen, dass sie die Entscheidung über die Sprache beeinflussen, in der Sie Ihr System implementieren.

Also mein Rat, optimieren Sie frühzeitig, basierend auf Ihren Anforderungen, nicht Ihrem Code, und achten Sie auf die mögliche längere Lebensdauer Ihrer App.


5
Ich bin nicht einverstanden mit Ihrer Schlussfolgerung, dass Sie es zu spät gelassen haben. Grundsätzlich ist eine Profilerstellung erforderlich, wenn eine Annahme nicht zutrifft, und der Profiler muss Ihnen mitteilen, welche Annahme gebrochen wurde. Zum Beispiel fand ich, dass "Zeichen an Position 0 löschen" für StringBuffers in Java für die Junit-Tests gut funktionierte, für große Strings jedoch SEHR langsam. Ich habe diesen Code nicht vermutet, bis der Profiler ihn als Schuldigen identifiziert hat!
Thorbjørn Ravn Andersen

7
Aufgrund meiner Erfahrung stimme ich dem "Wenn Sie den Profiler benötigen, ist es bereits zu spät" zu - die meisten meiner Leistungsprobleme sind keine einzelnen Engpässe, sondern verteilen sich auf mehrere Mitwirkende. Aber dann habe ich einen starken Hintergrund in Bezug auf Code und Kosten auf niedriger Ebene und hätte instinktiv alles gemieden, was auf der (deutlich wiederholten) Entfernung des ersten Zeichenfolgenzeichens beruht. +1 für "Während des Entwurfs optimieren".
Peterchen

@peterchen nur aus Neugier, was hätten Sie getan, um "das erste Zeichen zu entfernen".
Ghos3t

1
@ user258365: Brute Force besteht darin, eine Zeichenfolgendarstellung zu verwenden, für die keine Kopie für Unterzeichenfolgen erstellt werden muss. Das ist "fast trivial" für unveränderliche Zeichenfolgen mit Referenzzählung. Alternativ algorithmische Veränderungen, wie zu ersetzen (Pseudo - Code) while (s[0]==' ') s = s.substring(1) for(i=0; i<s.len && s[i]==' '; ++i); s=s.substring(i)--- aber das erfordert schon zu wissen , potenzielle Leistungsprobleme (Profilometer sind wertvolle Werkzeuge für ständiges Lernen hier).
Peterchen

@ ThorbjørnRavnAndersen, ich habe als Berater gearbeitet, um einem Team beim Abschluss eines Projekts zu helfen, aber es war nicht möglich, da keine schwerwiegenden Leistungsprobleme geplant waren (außer dem Spaghetti-Code). Es sollte eine chronologische Tabelle mit der Anamnese aller Patienten zeigen. Eine einzige Anfrage wurde für die gesamten Daten gestellt, wie Google Maps, das die ganze Welt abruft. Durch die Entwicklung von fehlerhaftem Code und die Erwartung einer späteren Profilerstellung schlug das Projekt fehl.
Pedro Amaral Couto

30

Wenn Sie kein Profil erstellt haben, ist es verfrüht.


3
Ich stimme der Idee dahinter zu, aber auch: Wenn die Implementierung nicht vollständig an CPU-Zyklen gebunden ist, ist es schwierig, eine Messung zu erhalten, die sowohl reproduzierbar als auch verallgemeinerbar ist - und je stabiler sie ist, desto weniger realistisch ist sie auch.
Peterchen

1
Das Problem, das ich mit der obigen Antwort habe, ist, dass es impliziert, dass Sie einen Algorithmus nicht optimieren können, bevor Sie ihn codieren. Meine Arbeitsweise besteht darin, den Algorithmus so zu gestalten, dass er den funktionalen Anforderungen entspricht. Überprüfen Sie, ob die Leistungsanforderungen wahrscheinlich nicht erfüllt werden (z. B. hohe Komplexität und wahrscheinlich große Datenmengen), und optimieren Sie den Algorithmus, bevor Sie mit dem Codieren beginnen. Optimierung ist einfach eine Verfeinerung, um eine optimale Lösung zu erreichen. Sie wird häufig in der Entwurfsphase am effizientesten durchgeführt.
SmacL

2
Ich stimme nicht zu Knuth sprach von kleinen Wirkungsgraden. Die Optimierung erfolgt häufig in der Entwurfsphase. Dabei werden geeignete Datenstrukturen und Algorithmen ausgewählt, die häufig große Auswirkungen auf die Leistung haben und später nicht unbedingt ausgetauscht werden können.
Haslersn

@haslersn: "Knuth sprach von kleinen Effizienzgewinnen" Donald Knuth: "Die konventionelle Weisheit, die viele der heutigen Software-Ingenieure teilen, verlangt, die Effizienz im Kleinen zu ignorieren; aber ich glaube, dies ist einfach eine Überreaktion auf die Missbräuche (...) etablierte Ingenieurdisziplinen eine 12% ige Verbesserung, die leicht zu erreichen ist, wird niemals als marginal angesehen (...) "
Pedro Amaral Couto

27

Meine Frage ist, ob eine bestimmte Technik anders, aber nicht besonders dunkel oder verschleiert ist, kann dies wirklich als vorzeitige Optimierung angesehen werden?

Ähm ... Sie haben also zwei Techniken zur Hand, die hinsichtlich der Kosten identisch sind (gleicher Aufwand beim Verwenden, Lesen, Ändern) und eine effizienter ist. Nein, die effizientere zu verwenden wäre in diesem Fall nicht verfrüht.

Wenn Sie das Schreiben von Code unterbrechen, um nach Alternativen zu gängigen Programmierkonstrukten / Bibliotheksroutinen zu suchen, besteht die Möglichkeit, dass irgendwo eine effizientere Version herumhängt, obwohl die relative Geschwindigkeit des Schreibens nach allem, was Sie wissen, eigentlich keine Rolle spielt. .. Das ist verfrüht.


3
Wenn Sie wissen, dass ein Algorithmus für Ihren Anwendungsfall effizienter ist, verwenden Sie auf jeden Fall den effizienteren. Wenn Sie den effizientesten Algorithmus nicht kennen, verwenden Sie das, was Sie haben, und das Profil später, um festzustellen, ob es sich um ein Problem handelt.
Grepsedawk

10

Hier ist das Problem, das ich mit dem gesamten Konzept der Vermeidung vorzeitiger Optimierung sehe.

Es gibt eine Trennung zwischen dem Sagen und dem Tun.

Ich habe viele Leistungsoptimierungen vorgenommen und große Faktoren aus ansonsten gut gestaltetem Code herausgepresst, scheinbar ohne vorzeitige Optimierung. Hier ist ein Beispiel.

In fast allen Fällen ist der Grund für die suboptimale Leistung das, was ich als galoppierende Allgemeinheit bezeichne. Dies ist die Verwendung abstrakter mehrschichtiger Klassen und eines gründlichen objektorientierten Designs, bei dem einfache Konzepte weniger elegant, aber völlig ausreichend wären .

Und in dem Lehrmaterial, in dem diese abstrakten Designkonzepte vermittelt werden, wie z. B. benachrichtigungsgesteuerte Architektur und Verstecken von Informationen, in denen das einfache Festlegen einer booleschen Eigenschaft eines Objekts einen unbegrenzten Welleneffekt von Aktivitäten haben kann, was ist der angegebene Grund? Effizienz .

War das eine vorzeitige Optimierung oder nicht?


Ich mag diese Antwort, da sie eines der Hauptprobleme bei der Abstraktion und Verallgemeinerung darstellt. Wenn Sie eine Klassenhierarchie verallgemeinern, um ein breiteres Spektrum von Anwendungsfällen zu unterstützen, ist es allzu leicht, die Leistung für die typischsten Anwendungsfälle ernsthaft zu beeinträchtigen. Es ist auch einfach, sich an eine Klasse zu binden, die eine bestimmte Funktionalität bereitstellt, ohne zu prüfen, ob die Funktionalität für den Umfang der beabsichtigten Verwendung auf einem akzeptablen Leistungsniveau bereitgestellt wird.
SmacL

1
"Wo einfache Konzepte weniger elegant, aber völlig ausreichend wären " Komplexer Code ist selten eleganter als einfacher Code, wenn einfacher Code die Anforderungen erfüllt. (Obwohl ich argumentieren würde, dass Sie sicherstellen müssen, dass Ihr einfacher Code tatsächlich mit einem klaren Hinweis auf den nicht unterstützten Status / die nicht unterstützte Eingabe explodiert, wenn jemand versucht, ihn in einem komplexeren Fall auszuführen.)
jpmc26

8

Lassen Sie zuerst den Code funktionieren. Zweitens überprüfen Sie, ob der Code korrekt ist. Drittens machen Sie es schnell.

Jeder Code Änderung , die vor der Bühne # getan wird 3 ist auf jeden Fall verfrüht. Ich bin mir nicht ganz sicher, wie ich die zuvor getroffenen Entwurfsentscheidungen klassifizieren soll (wie die Verwendung gut geeigneter Datenstrukturen), aber ich bevorzuge die Verwendung von Abstraktionen, mit denen man einfach programmieren kann, anstatt mit denen, die eine gute Leistung erbringen, bis ich bei bin Eine Phase, in der ich mit der Profilerstellung beginnen kann und eine korrekte (wenn auch häufig langsame) Referenzimplementierung zum Vergleichen der Ergebnisse habe.


7

Sie sprechen anscheinend von einer Optimierung wie der Verwendung eines Hash-basierten Suchcontainers im Vergleich zu einem indizierten Container wie einem Array, wenn viele wichtige Suchvorgänge durchgeführt werden. Dies ist keine vorzeitige Optimierung, sondern etwas, das Sie in der Entwurfsphase entscheiden sollten.

Die Art der Optimierung, um die es in der Knuth-Regel geht, besteht darin, die Länge der am häufigsten verwendeten Codepfade zu minimieren, den Code zu optimieren, der am häufigsten ausgeführt wird, indem beispielsweise in der Assembly neu geschrieben oder der Code vereinfacht wird, wodurch er weniger allgemein wird. Dies hat jedoch keinen Sinn, bis Sie sicher sind, welche Teile des Codes diese Art der Optimierung benötigen, und die Optimierung wird (könnte?) Das Verständnis oder die Wartung des Codes erschweren. Daher ist "vorzeitige Optimierung die Wurzel allen Übels".

Knuth sagt auch, dass es immer besser ist, anstatt zu optimieren, die von Ihrem Programm verwendeten Algorithmen und die Herangehensweise an ein Problem zu ändern. Während durch eine kleine Optimierung die Geschwindigkeit durch Optimierung um 10% gesteigert werden kann, kann eine grundlegende Änderung der Funktionsweise Ihres Programms zu einer 10-fachen Geschwindigkeit führen.

Als Reaktion auf viele andere Kommentare zu dieser Frage: Algorithmusauswahl! = Optimierung


7

Aus Datenbanksicht ist es bestenfalls tollkühn, in der Entwurfsphase kein optimales Design zu berücksichtigen. Datenbanken lassen sich nicht einfach umgestalten. Sobald sie schlecht entworfen sind (dies ist ein Design, das die Optimierung nicht berücksichtigt, egal wie Sie versuchen, sich hinter dem Unsinn der vorzeitigen Optimierung zu verstecken), kann es fast nie wiederhergestellt werden, da die Datenbank zu grundlegend für das ist Betrieb des gesamten Systems. Es ist weitaus kostengünstiger, unter Berücksichtigung des optimalen Codes für die von Ihnen erwartete Situation korrekt zu entwerfen, als zu warten, bis eine Million Benutzer vorhanden sind und die Leute schreien, weil Sie in der gesamten Anwendung Cursor verwendet haben. Andere Optimierungen wie die Verwendung von sargeable Code, die Auswahl der bestmöglichen Indizes usw. sind nur zur Entwurfszeit sinnvoll. Es gibt einen Grund, warum schnell und schmutzig so genannt wird. Da es nie gut funktionieren kann, sollten Sie die Schnelligkeit nicht als Ersatz für guten Code verwenden. Wenn Sie die Leistungsoptimierung in Datenbanken verstehen, können Sie auch Code schreiben, der mit größerer Wahrscheinlichkeit zur gleichen Zeit oder weniger gut funktioniert als das Schreiben von Code, der nicht gut funktioniert. Sich nicht die Zeit zu nehmen, um zu lernen, was ein leistungsfähiges Datenbankdesign ist, ist Entwicklerfaulheit, keine bewährte Methode.


6

Der Punkt der Maxime ist, dass die Optimierung typischerweise kompliziert und komplex ist. In der Regel benötigen Sie als Architekt / Designer / Programmierer / Betreuer klaren und präzisen Code, um zu verstehen, was vor sich geht.

Wenn eine bestimmte Optimierung klar und präzise ist, können Sie gerne damit experimentieren (gehen Sie jedoch zurück und prüfen Sie, ob diese Optimierung wirksam ist). Es geht darum, den Code während des gesamten Entwicklungsprozesses klar und präzise zu halten, bis die Vorteile der Leistung die Kosten für das Schreiben und Verwalten der Optimierungen überwiegen.


2
Tatsächlich läuft ein gutes Stück "Optimierung" darauf hinaus, den richtigen Algorithmus für den Job auszuwählen. Es handelt sich um eine Aktivität auf hoher Ebene mit Ergebnissen auf hoher Ebene - weit entfernt von den "kleinen Wirkungsgraden" im Knuth-Zitat.
Shog9

4

Ich versuche nur zu optimieren, wenn ein Leistungsproblem bestätigt wird.

Meine Definition der vorzeitigen Optimierung lautet "Aufwand für Code, von dem nicht bekannt ist, dass er ein Leistungsproblem darstellt". Es gibt definitiv einen Zeitpunkt und einen Ort für die Optimierung. Der Trick besteht jedoch darin, die zusätzlichen Kosten nur dort auszugeben, wo sie für die Leistung der Anwendung von Bedeutung sind und wo die zusätzlichen Kosten den Leistungseinbruch überwiegen.

Beim Schreiben von Code (oder einer DB-Abfrage) bemühe ich mich, "effizienten" Code zu schreiben (dh Code, der seine beabsichtigte Funktion schnell und vollständig mit der einfachsten vernünftigen Logik ausführt). Beachten Sie, dass "effizienter" Code nicht unbedingt mit "optimiert" identisch ist. Code. Optimierungen führen häufig zu einer zusätzlichen Komplexität des Codes, was sowohl die Entwicklungs- als auch die Wartungskosten dieses Codes erhöht.

Mein Rat: Versuchen Sie, die Kosten für die Optimierung nur dann zu bezahlen, wenn Sie den Nutzen quantifizieren können.


4

Bei der Programmierung sind eine Reihe von Parametern von entscheidender Bedeutung. Unter diesen sind:

  • Lesbarkeit
  • Wartbarkeit
  • Komplexität
  • Robustheit
  • Richtigkeit
  • Performance
  • Entwicklungszeit

Die Optimierung (auf Leistung ausgerichtet) geht häufig zu Lasten anderer Parameter und muss gegen den "Verlust" in diesen Bereichen abgewogen werden.

Wenn Sie die Möglichkeit haben, bekannte Algorithmen auszuwählen, die eine gute Leistung erbringen, sind die Kosten für die "Optimierung" im Voraus oft akzeptabel.


1
Ihnen fehlt der wichtigste QS-Parameter in Ihrer obigen Liste. Anforderungen erfüllen. Wenn eine Software nicht den Anforderungen des Zielpublikums entspricht, sind alle anderen Parameter bedeutungslos. Wenn die Leistung nicht akzeptabel ist, wurden die Anforderungen nicht erfüllt.
SmacL

3
Man könnte sagen, dass dies durch Korrektheit abgedeckt ist. Außerdem gehört „Leistung“ im Sinne von „so schnell wie möglich“ nur sehr selten zu den Anforderungen, und selbst Ola wollte, dass ein Kompromiss mit den anderen Anforderungen besteht.
Frankodwyer

4

Die Optimierung kann auf verschiedenen Granularitätsstufen erfolgen, von sehr hohen bis zu sehr niedrigen Ebenen:

  1. Beginnen Sie mit einer guten Architektur, loser Kopplung, Modularität usw.

  2. Wählen Sie die richtigen Datenstrukturen und Algorithmen für das Problem.

  3. Optimieren Sie den Speicher und versuchen Sie, mehr Code / Daten in den Cache einzufügen. Das Speichersubsystem ist 10 bis 100 Mal langsamer als die CPU. Wenn Ihre Daten auf die Festplatte ausgelagert werden, ist es 1000 bis 10.000 Mal langsamer. Vorsicht beim Speicherverbrauch führt eher zu größeren Vorteilen als zur Optimierung einzelner Anweisungen.

  4. Verwenden Sie in jeder Funktion die Anweisungen zur Flusskontrolle in geeigneter Weise. (Verschieben Sie unveränderliche Ausdrücke außerhalb des Schleifenkörpers. Setzen Sie den häufigsten Wert in einem Schalter / Fall usw. an die erste Stelle.)

  5. Verwenden Sie in jeder Anweisung die effizientesten Ausdrücke, um das richtige Ergebnis zu erzielen. (Multiplizieren vs. Verschieben usw.)

Es ist nicht unbedingt notwendig , zu entscheiden, ob ein Divide-Ausdruck oder ein Shift-Ausdruck verwendet werden soll vorzeitige Optimierung. Es ist nur verfrüht, wenn Sie dies tun, ohne zuvor die Architektur, Datenstrukturen, Algorithmen, den Speicherbedarf und die Flusskontrolle zu optimieren.

Und natürlich ist jede Optimierung verfrüht, wenn Sie keinen Zielleistungsschwellenwert definieren.

In den meisten Fällen entweder:

A) Sie können den Zielleistungsschwellenwert erreichen, indem Sie Optimierungen auf hoher Ebene durchführen. Es ist also nicht erforderlich, mit den Ausdrücken herumzuspielen.

oder

B) Selbst nachdem Sie alle möglichen Optimierungen durchgeführt haben, werden Sie Ihren Zielleistungsschwellenwert nicht erreichen, und die Optimierungen auf niedriger Ebene machen keinen ausreichenden Leistungsunterschied, um den Verlust der Lesbarkeit zu rechtfertigen.

Nach meiner Erfahrung können die meisten Optimierungsprobleme entweder auf der Ebene der Architektur / des Designs oder der Datenstruktur / des Algorithmus gelöst werden. Die Optimierung des Speicherbedarfs ist häufig (wenn auch nicht immer) erforderlich. Es ist jedoch selten erforderlich, die Flusssteuerungs- und Ausdruckslogik zu optimieren. Und in den Fällen, in denen es tatsächlich notwendig ist, reicht es selten aus.


3

Die Notwendigkeit, einen Profiler zu verwenden, sollte in extremen Fällen bestehen bleiben. Die Ingenieure des Projekts sollten wissen, wo Leistungsengpässe liegen.

Ich denke, "vorzeitige Optimierung" ist unglaublich subjektiv.

Wenn ich Code schreibe und ich weiß dass ich eine Hashtabelle verwenden sollte, werde ich das tun. Ich werde es nicht fehlerhaft implementieren und dann warten, bis der Fehlerbericht einen Monat oder ein Jahr später eintrifft, wenn jemand ein Problem damit hat.

Redesign ist teurer als die Optimierung eines Designs auf offensichtliche Weise von Anfang an.

Natürlich werden einige kleine Dinge beim ersten Mal übersehen, aber dies sind selten wichtige Designentscheidungen.

Deshalb: Ein Design NICHT zu optimieren ist IMO ein Code-Geruch an und für sich.


Es kommt vor, dass Engpässe häufig in Codeabschnitten auftreten, von denen Sie nie gedacht haben, dass sie ein Problem darstellen. Die Profilerstellung verzichtet auf Täuschung und zeigt die tatsächlichen Kostenstellen des Programms. Es ist am besten, von Anfang an offensichtliche Dinge zu tun, aber für alles andere gibt es Profilerstellung.
Chris Smith

2

Normans Antwort ist ausgezeichnet. Irgendwie führen Sie routinemäßig eine "vorzeitige Optimierung" durch, die eigentlich Best Practices sind, da bekannt ist, dass eine andere Vorgehensweise völlig ineffizient ist.

Zum Beispiel, um zu Normans Liste hinzuzufügen:

  • Verwenden der StringBuilder-Verkettung in Java (oder C # usw.) anstelle von String + String (in einer Schleife);
  • Vermeiden, in C wie for (i = 0; i < strlen(str); i++)folgt zu schleifen: (da strlen hier ein Funktionsaufruf ist, der jedes Mal durch den String läuft und in jeder Schleife aufgerufen wird);
  • Es scheint in den meisten JavaScript-Implementierungen auch schneller zu gehen for (i = 0 l = str.length; i < l; i++)und es ist immer noch lesbar, also OK.

Und so weiter. Solche Mikrooptimierungen sollten jedoch niemals auf Kosten der Lesbarkeit von Code gehen.


2

Es ist erwähnenswert, dass Knuths ursprüngliches Zitat aus einem Artikel stammt, den er verfasst hat, um die Verwendung gotoin sorgfältig ausgewählten und gemessenen Bereichen zu fördern , um Hotspots zu beseitigen. Sein Zitat war eine Einschränkung, die er hinzufügte, um seine Gründe für die gotoBeschleunigung dieser kritischen Schleifen zu rechtfertigen .

[...] Dies ist wiederum eine spürbare Einsparung der Gesamtlaufgeschwindigkeit, wenn beispielsweise der Durchschnittswert von n etwa 20 beträgt und die Suchroutine etwa eine Million Mal im Programm ausgeführt wird. Solche Schleifenoptimierungen gotossind nicht schwer zu erlernen und, wie ich bereits sagte, nur für einen kleinen Teil eines Programms geeignet, führen jedoch häufig zu erheblichen Einsparungen. [...]

Und weiter:

Die konventionelle Weisheit, die viele der heutigen Softwareentwickler teilen, erfordert, die Effizienz im Kleinen zu ignorieren. Aber ich glaube, dies ist einfach eine Überreaktion auf die Missbräuche, die von Pennywise-and-Pound-Dummkopf-Programmierern praktiziert werden, die ihre "optimierten" Programme nicht debuggen oder warten können. In etablierten Ingenieurdisziplinen wird eine leicht zu erreichende Verbesserung von 12% niemals als marginal angesehen. und ich glaube, dass der gleiche Standpunkt in der Softwareentwicklung vorherrschen sollte. Natürlich würde ich mich nicht darum kümmern, solche Optimierungen bei einem One-Shot-Job vorzunehmen, aber wenn es um die Vorbereitung von Qualitätsprogrammen geht, möchte ich mich nicht auf Tools beschränken, die mir solche Effizienz verweigern [dh goto Aussagen in diesem Zusammenhang].

Denken Sie daran, wie er in Anführungszeichen "optimiert" verwendet hat (die Software ist wahrscheinlich nicht wirklich effizient). Beachten Sie auch, dass er nicht nur diese "Pennywise-and-Pound-Dummkopf" -Programmierer kritisiert, sondern auch die Leute, die darauf reagieren, indem sie vorschlagen, dass Sie kleine Ineffizienzen immer ignorieren sollten. Zum abschließenden Teil:

Es besteht kein Zweifel, dass der Gral der Effizienz zu Missbrauch führt. Programmierer verschwenden enorm viel Zeit damit, über die Geschwindigkeit unkritischer Teile ihrer Programme nachzudenken oder sich darüber Gedanken zu machen, und diese Effizienzversuche wirken sich tatsächlich stark negativ aus, wenn Debugging und Wartung in Betracht gezogen werden. Wir sollten kleine Wirkungsgrade vergessen, sagen wir 97% der Zeit; Vorzeitige Optimierung ist die Wurzel allen Übels.

... und noch etwas mehr über die Bedeutung von Profiling-Tools:

Es ist oft ein Fehler, a priori zu beurteilen, welche Teile eines Programms wirklich kritisch sind, da die universelle Erfahrung von Programmierern, die Messwerkzeuge verwendet haben, darin bestand, dass ihre intuitiven Vermutungen fehlschlagen. Nachdem ich sieben Jahre lang mit solchen Tools gearbeitet habe, bin ich davon überzeugt, dass alle von nun an geschriebenen Compiler so konzipiert sein sollten, dass sie allen Programmierern Feedback geben, welche Teile ihrer Programme am meisten kosten. In der Tat sollte dieses Feedback automatisch bereitgestellt werden, es sei denn, es wurde speziell deaktiviert.

Die Leute haben sein Zitat überall missbraucht und oft darauf hingewiesen, dass Mikrooptimierungen verfrüht sind, als sein gesamtes Papier Mikrooptimierungen befürwortete! Eine der von ihm kritisierten Personengruppen, die diese "konventionelle Weisheit" wiederholen, weil er die Effizienz im Kleinen immer ignoriert, missbraucht häufig sein Zitat, das ursprünglich teilweise gegen solche Typen gerichtet war, die alle Formen der Mikrooptimierung abschrecken .

Es war jedoch ein Zitat zugunsten angemessen angewandter Mikrooptimierungen, wenn diese von einer erfahrenen Hand mit einem Profiler verwendet wurden. Das heutige analoge Äquivalent könnte lauten: "Menschen sollten bei der Optimierung ihrer Software keine blinden Stiche machen, aber benutzerdefinierte Speicherzuordnungen können einen großen Unterschied machen, wenn sie in Schlüsselbereichen angewendet werden, um die Referenzlokalität zu verbessern" oder " Handgeschriebener SIMD-Code mit einem SoA rep ist wirklich schwer zu pflegen und sollte nicht überall verwendet werden, aber es kann viel schneller Speicher verbrauchen, wenn es von einer erfahrenen und geführten Hand angemessen angewendet wird. "

Jedes Mal, wenn Sie versuchen, sorgfältig angewandte Mikrooptimierungen zu fördern, wie Knuth es oben beworben hat, ist es gut, einen Haftungsausschluss einzufügen, um Neulinge davon abzuhalten, zu aufgeregt zu werden und blindlings Optimierungen vorzunehmen, z. B. ihre gesamte Software für die Verwendung neu zu schreiben goto. Das war zum Teil das, was er tat. Sein Zitat war praktisch Teil eines großen Haftungsausschlusses, genau wie jemand, der ein Motorrad über eine brennende Feuerstelle springt, einen Haftungsausschluss hinzufügen könnte, dass Amateure dies nicht zu Hause versuchen sollten, während sie gleichzeitig diejenigen kritisieren, die es ohne angemessenes Wissen und Ausrüstung versuchen und verletzt werden .

Was er als "vorzeitige Optimierungen" ansah, waren Optimierungen, die von Leuten angewendet wurden, die effektiv nicht wussten, was sie taten: Sie wussten nicht, ob die Optimierung wirklich benötigt wurde, maßen nicht mit geeigneten Werkzeugen, verstanden vielleicht nicht die Natur von Ihre Compiler- oder Computerarchitektur und vor allem ihre "Pennywise-and-Pound-Dummheit" waren, was bedeutete, dass sie die großen Möglichkeiten zur Optimierung (Einsparung von Millionen von Dollar) übersehen, indem sie versuchten, ein paar Cent zu kneifen, und während sie Code erstellten, den sie nicht können länger effektiv debuggen und warten.

Wenn Sie nicht in die Kategorie "Pennywise-and-Pound-Dummkopf" passen, optimieren Sie nicht vorzeitig nach Knuths Maßstäben, selbst wenn Sie a gotoverwenden, um eine kritische Schleife zu beschleunigen (was unwahrscheinlich ist) um viel gegen die heutigen Optimierer zu helfen, aber wenn es so wäre und in einem wirklich kritischen Bereich, dann würden Sie nicht vorzeitig optimieren). Wenn Sie tatsächlich alles anwenden, was Sie in Bereichen tun, die wirklich gebraucht werden und von denen sie wirklich profitieren, dann sind Sie in den Augen von Knuth einfach großartig.


1

Vorzeitige Optimierung bedeutet für mich, die Effizienz Ihres Codes zu verbessern, bevor Sie ein funktionierendes System haben und bevor Sie es tatsächlich profiliert haben und wissen, wo der Engpass liegt. Auch danach sollten in vielen Fällen Lesbarkeit und Wartbarkeit vor der Optimierung liegen.


1

Ich denke nicht, dass anerkannte Best Practices vorzeitige Optimierungen sind. Es geht mehr um die Brenndauer der Was-wäre-wenn-Fälle, bei denen es sich je nach Nutzungsszenario um potenzielle Leistungsprobleme handelt. Ein gutes Beispiel: Wenn Sie eine Woche lang versuchen, die Reflexion über ein Objekt zu optimieren, bevor Sie den Beweis haben, dass es sich um einen Engpass handelt, optimieren Sie vorzeitig.


1

Wenn Sie nicht feststellen, dass Ihre Anwendung aufgrund eines Benutzer- oder Geschäftsbedarfs mehr Leistung benötigt, gibt es wenig Grund zur Sorge um die Optimierung. Tun Sie auch dann nichts, bis Sie Ihren Code profiliert haben. Greife dann die Teile an, die am meisten Zeit in Anspruch nehmen.


0

So wie ich das sehe, ist es eine vorzeitige Optimierung, wenn Sie etwas optimieren, ohne zu wissen, wie viel Leistung Sie in verschiedenen Szenarien erzielen können. Das Ziel von Code sollte es dem Menschen wirklich leichter machen, ihn zu lesen.


0

Wie ich zu einer ähnlichen Frage geschrieben habe, gelten folgende Optimierungsregeln:

1) Nicht optimieren

2) (nur für Experten) Später optimieren

Wann ist die Optimierung verfrüht? Normalerweise.

Die Ausnahme liegt möglicherweise in Ihrem Design oder in gut gekapseltem Code, der häufig verwendet wird. In der Vergangenheit habe ich an zeitkritischem Code (einer RSA-Implementierung) gearbeitet, bei dem der vom Compiler erstellte Assembler und das Entfernen eines einzelnen unnötigen Befehls in einer inneren Schleife eine 30% ige Beschleunigung ergab. Die Beschleunigung durch die Verwendung komplexerer Algorithmen war jedoch um Größenordnungen höher.

Eine andere Frage, die Sie sich bei der Optimierung stellen sollten, lautet: "Mache ich hier das Äquivalent zur Optimierung für ein 300-Baud-Modem?" . Mit anderen Worten, wird Moores Gesetz Ihre Optimierung in Kürze irrelevant machen. Viele Skalierungsprobleme können gelöst werden, indem mehr Hardware auf das Problem geworfen wird.

Last but not least ist es verfrüht zu optimieren, bevor das Programm zu langsam läuft. Wenn es sich um eine Webanwendung handelt, über die Sie sprechen, können Sie sie unter Last ausführen, um festzustellen, wo die Engpässe liegen. Es ist jedoch wahrscheinlich, dass Sie dieselben Skalierungsprobleme wie die meisten anderen Websites haben und dieselben Lösungen gelten.

edit: Übrigens würde ich in Bezug auf den verlinkten Artikel viele der getroffenen Annahmen in Frage stellen. Erstens ist es nicht wahr, dass Moores Gesetz in den 90er Jahren aufgehört hat zu arbeiten. Zweitens ist es nicht offensichtlich, dass die Zeit des Benutzers wertvoller ist als die Zeit des Programmierers. Die meisten Benutzer nutzen (gelinde gesagt) nicht jeden verfügbaren CPU-Zyklus, sondern warten wahrscheinlich darauf, dass das Netzwerk etwas unternimmt. Außerdem entstehen Opportunitätskosten, wenn die Zeit des Programmierers von der Implementierung von etwas anderem abgelenkt wird, um ein paar Millisekunden von etwas zu sparen, das das Programm tut, während der Benutzer am Telefon ist. Alles, was länger ist, ist normalerweise keine Optimierung, sondern eine Fehlerbehebung.

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.