In den "guten alten Tagen", als wir Shareware für Freunde auf Disketten kopierten, verwendeten wir auch einiges an Montage. Es gab eine gängige Praxis der "Mikrooptimierung", bei der Sie die Montagelinien anstarrten und anstarrten, bis Sie eine Möglichkeit gefunden hatten, sie in einer Anweisung weniger auszudrücken. Es gab sogar ein Sprichwort, das mathematisch unmöglich war: „ Sie können immer noch eine Anweisung entfernen. “ Da die Änderung der Laufzeitleistung durch kleine konstante Faktoren für die (meisten) Programmierer heutzutage kein großes Problem darstellt, übertragen Programmierer diese Mikrobefehle. Optimierungsbemühungen woanders?
Mit anderen Worten: Kann eine bewährte Methode in einen extremen Zustand versetzt werden, in dem sie keinen Mehrwert mehr bietet? Und verschwendet stattdessen Zeit?
Zum Beispiel: Verschwenden Programmierer Zeit damit, private Methoden zu generalisieren , die nur von einer Stelle aus aufgerufen werden? Wird Zeit verschwendet, um die Testfalldaten zu reduzieren? Sind Programmierer (immer noch) übermäßig besorgt, Codezeilen zu reduzieren?
Im Folgenden finden Sie zwei gute Beispiele für das, wonach ich suche: (1) Zeit damit verbringen, die richtigen Variablennamen zu finden und sogar alles umzubenennen; und (2) Entfernen auch geringfügiger und schwacher Codeduplizierungen.
Beachten Sie, dass sich dies von der Frage " Wofür optimieren Sie? " Unterscheidet , da ich frage, was andere Programmierer zu maximieren scheinen. Das Stigma dabei sind "Mikro" -Optimierungen und daher keine produktive Nutzung der Zeit.
quelle
Antworten:
Code-Formatierung
quelle
=
und die vierte für die Initialisierer!Früher habe ich viel Assembler geschrieben. Es ist nicht nur so, dass die Compiler besser geworden sind, es ist auch so, dass die meiste Hardware jetzt eine Menge Logik hat, die der Ausführung von Code außerhalb der Reihenfolge gewidmet ist. Das eigentliche Kleinstproblem ist die Zeitplanung. Die meisten Computeranweisungen benötigen mehrere Maschinentakte, um ein Ergebnis zu erzielen. Eine Speicherbelastung, die den Cache verfehlt, kann mehrere Hundert dauern. Die Idee war also, andere Anweisungen einzuplanen, um etwas Nützliches zu tun, anstatt auf ein Ergebnis zu warten. Und moderne Maschinen können mehrere Befehle pro Taktperiode ausgeben. Als wir anfingen, HW außer Betrieb zu setzen, stellte ich fest, dass der Versuch, mit Handcodierung eine großartige Leistung zu erzielen, zu einem Becherspiel wurde. Erstens würde die außer Betrieb befindliche HW die Anweisungen nicht in Ihrer sorgfältig ausgearbeiteten Reihenfolge ausführen. Die ausgefallene neue HW-Architektur hat den Nachteil einer unzureichenden Software-Planung so weit verringert, dass der Compiler in der Regel nur wenige Prozent Ihrer Leistung erbrachte. Außerdem fand ich heraus, dass Compiler mittlerweile bekannte, aber komplexitätserzeugende Tricks implementieren, wie z. B. Abrollen, Laden von unten, Software-Pipelining usw. Unter dem Strich muss man wirklich hart arbeiten, einige dieser Tricks überspringen und der Compiler schlägt dich. Verwenden Sie sie alle und die Anzahl der benötigten Assembler-Anweisungen erhöht sich um ein Vielfaches!
Noch wichtiger ist wahrscheinlich, dass es bei den meisten Leistungsproblemen nicht um die Anzahl der Befehlsausgaben geht, sondern darum, die Daten in die CPU zu übertragen. Wie oben erwähnt, beträgt die Speicherlatenz jetzt Hunderte von Zyklen, und die CPU kann mehrere Befehle pro Taktperiode ausführen. Sofern das Programm und insbesondere die Datenstrukturen nicht so ausgelegt sind, dass die Cachetrefferrate bei dem Befehl übermäßig hoch ist, erfolgt eine Mikroabstimmung Level wird keine Auszahlung haben. Genau wie militärische Typen sagen, Amateure reden über Taktik, Profis reden über Logistik. Leistungsprogrammierung ist jetzt mehr als 90% Logistik (bewegte Daten). Und dies ist schwer zu quantifizieren, da die moderne Speicherverwaltung in der Regel mehrere Cache-Ebenen aufweist und virtuelle Speicherseiten von einer als TLB bezeichneten Hardwareeinheit verarbeitet werden. Auch die Ausrichtung von Adressen auf niedriger Ebene wird wichtig, da die tatsächlichen Datenübertragungen nicht in Byte-Einheiten erfolgen. oder sogar 64-Bit-Long-Longs, aber sie kommen in Einheiten von Cache-Zeilen. Dann verfügen die meisten modernen Maschinen über Hardware, die versucht, vorherzusagen, welche Cache-Zeilen in naher Zukunft möglicherweise nicht benötigt werden, und automatische Prefetches ausgibt, um sie in den Cache zu bekommen. Die Realität ist also, dass die Leistungsmodelle bei modernen CPUs so komplex sind, dass sie fast unverständlich sind. Sogar detaillierte Hardware-Simulatoren können niemals mit der exakten Logik der Chips mithalten, so dass eine exakte Abstimmung einfach nicht mehr möglich ist.
Es ist immer noch Platz für eine Handcodierung. Mathematische Bibliotheken (wie die Exp-Funktion) sowie die wichtigeren linearen Algebra-Operationen (wie Matrixmultiplikation) werden in der Regel noch von Experten handcodiert, die für den Hardwareanbieter (z. B. Intel, AMD oder IBM) arbeiten, aber wahrscheinlich nur Ich brauche ein paar erstklassige Assembler-Programmierer pro Megacomputer.
quelle
float
. Aus irgendeinem Grund kann ich nicht verstehen, viele Leute denken, dass dies eine gute Sache ist.Manchmal verbringe ich (Zeitverschwendung?) Zeit damit, einen guten Namen für eine Variable oder eine Methode zu wählen, damit sie nicht nur genau beschreibend ist, sondern auch einen guten sprachlichen Stil hat.
Es geht etwas weiter, wenn ich versuche, die gesamten Werke (ein Programm) in den gleichen sprachlichen Stil zu bringen. Manchmal ändert sich meine Meinung und ich überarbeite das Buch. :)
Nein, das dauert so lange. Es ist eher selten. Aber ich mag es, die gute Grammatik in meinen Programmen zu behalten.
quelle
Zeitliche Komplexität. Ich verbringe viel zu viel Zeit damit, Dinge für die Leistung im schlimmsten Fall auf einer Skala zu optimieren, die um Größenordnungen größer ist als alles, von dem ich weiß, dass es dem Programm realistisch begegnen wird.
Ich bin einfach zu besessen, um das "aber es könnte diesen großen" Knochen wachsen zu lassen , selbst wenn andere Designentscheidungen dies von einem realistischen Geschehen ausschließen.
Sollte jedoch zu meiner Verteidigung das Unrealistische Realität werden, ist die Optimierung wirklich nicht mehr 'mikro'. Beachten Sie, ich sagte nicht unmöglich , aber unrealistisch .
Natürlich haben die Anforderungen Vorrang.
quelle
Ich glaube, ich habe Wochen damit verbracht, mich mit Java-Ausnahmehandlern zu beschäftigen.
Dies ist eine Menge Arbeit für Code, der zwei- oder dreimal pro Jahr fehlschlägt. Sollte dies eine Warnung oder eine Information sein? Fehler oder tödlich? Ist es wirklich fatal, dass der Prozess in fünf Minuten erneut gestartet wird? Ist es wirklich eine Warnung, wenn sich noch alles im Auslieferungszustand befindet?
Viel Nabelschau und Diskussion über die Art eines E / A-Fehlers. Wenn Sie eine Datei nicht über das Netzwerk lesen können, liegt ein Dateifehler oder ein Netzwerkfehler vor? Und so weiter.
Irgendwann habe ich alle Concat-Zeichenfolgen durch ersetzt,
String.format
damit der jährliche Dateifehler nicht zu zusätzlichen Objekten im Heap führt. Das war eine Verschwendung.quelle
Ich befasse mich wahrscheinlich zu sehr mit LOCs. Nicht so viele LOCs selbst, sondern mehr Anweisungen und noch mehr doppelte Anweisungen. Ich bin allergisch gegen Code-Duplikate. Im Allgemeinen liebe ich das Refactoring, aber ich nehme an, dass ungefähr 50% von dem, was ich mache, den Code nicht wesentlich schöner macht.
quelle
Lesen Sie eine Datei Zeile für Zeile, anstatt nur die gesamte Zeile in eine Zeichenfolge einzulesen und die Zeichenfolge in einem Take zu verarbeiten.
Sicher, es macht einen Unterschied in der Ausführungsgeschwindigkeit, ist aber selten die zusätzlichen Codezeilen wert. Es macht den Code weit weniger wartbar und vergrößert den Code.
Ja, das macht wahrscheinlich keinen Sinn, wenn die Datei 3 GB groß ist, aber die meisten Dateien sind nicht so groß (zumindest nicht die, mit denen ich arbeite ;-)).
quelle
Als ich Assemblersprache schrieb, war es sinnvoll, keine Bytes und Zyklen auszuwählen, aber das ist lange her und Compiler haben seitdem einen langen Weg zurückgelegt. Ich würde jetzt nicht versuchen, eine manuell zu optimieren.
Als ich zum Schreiben in C wechselte, war es ziemlich einfach, einen besseren Job als die C-Compiler zu machen, aber jedes Jahr veröffentlichten Borland oder Microsoft oder jemand anderes ein neues und verbessertes, das das vorherige im Raum herumwirbelte. Ich fing an, auf den tatsächlichen Assembler-Code zu achten, den der Compiler ausgibt, und, wenn er keinen netten und engen Code schreibt, Schleifen abwickelt, Variablen außerhalb der Schleifen verschiebt usw.
Heute schreibe ich in viel höheren Sprachen wie Perl, Python und Ruby. Ich benutze einige der Compiler-Tricks, wie das Abrollen von Schleifen, wenn es sinnvoll ist, das Verschieben statischer Variablen außerhalb der Schleifen usw., aber ich mache mir darüber nicht so viele Sorgen, weil CPUs jetzt ein kleines bisschen schneller sind. Wenn sich eine App unerwartet zu bewegen scheint, benutze ich einen Profiler, um zu sehen, was ich finden kann. Wenn sich etwas verbessern lässt, beginne ich mit dem Benchmarking verschiedener Methoden, mit denen ich mir vorstellen kann, etwas schneller zu machen, und sehe dann, wie es funktioniert skaliert.
Im Allgemeinen versuche ich, klug zu sein, wie ich Code schreibe, basierend auf jahrelanger Erfahrung.
quelle
Eine Mikrooptimierung: Verbesserung der Single-Thread-Leistung von Dingen, die parallelisierbar sind oder die einfach mit neuer Hardware verbessert werden können.
Wenn auf einem Computer vor 10 Jahren etwas langsam ist, ist es wahrscheinlich eine billigere Lösung, einen neueren, schnelleren Computer zu kaufen, anstatt Zeit für die Optimierung des Programmierers zu verschwenden. Ein großer Fokus der Optimierungen sollte jedoch darauf liegen, einen Weg zu finden, die 8 Kerne, die Sie heute bekommen können, oder die 64, die Sie in ein paar Jahren bekommen können, zu verwenden, anstatt sich über Kleinigkeiten wie die zusätzlichen Kosten für Müll zu quälen Sammlung.
quelle
Die Mikrooptimierung im Hinblick auf die Laufzeitleistung ist auch heute kaum ein geschlossenes Problem (obwohl dies weniger häufig vorkommt). Ich muss den Leuten gelegentlich erklären, dass der Aufwand für die Zuweisung eines zusätzlichen Objekts für jede Anforderung oder das Hinzufügen eines zusätzlichen Funktionsaufrufs vernachlässigbar ist.
Abgesehen davon sehe ich auch Programmierer, die der Einfachheit halber (einschließlich meiner selbst) Mikrooptimierungen vornehmen. Ich muss noch an einem Ort arbeiten, an dem ich den Unterschied zwischen Einfachheit und Einfachheit nicht erklären musste.
quelle
programmers.
=programmers.stackexchange.com
anders sein von jedem Ort , dass Sie haben gearbeitet , wo Sie gehabt haben , den Unterschied zwischen diesen beiden Begriffen zu erklären? Bitte erläutern Sie den Unterschied.Ich bin alle für das Prinzip "Nicht optimieren, es sei denn, es ist wirklich notwendig". Und ich bin alle für das Prinzip des ersten Profils. Und im Prinzip werde ich nur etwas optimieren, das den nötigen Unterschied ausmacht.
Das ist im Prinzip so. Aber das Prinzip ist ein Lügner.
Ich habe die Angewohnheit, auf Funktionen in häufig verwendeten Bibliotheken zu achten, von denen ich denke, dass sie in inneren Schleifen häufig verwendet werden. Und wenn Sie dann etwas in einer anderen Funktion entdecken, das nicht so oft verwendet wird ...
Oh - und dann gibt es die Logik "Ich schreibe es - natürlich ist es eine wichtige Bibliothek".
Oh - und übrigens. Ich habe noch nie einen Profiler verwendet, um eine Optimierung durchzuführen. Habe keinen Zugang zu einem kommerziellen und habe nie die Motivation aufgebaut, gprof herauszufinden.
Grundsätzlich bin ich ein Heuchler. Diese Prinzipien gelten für andere Leute, aber ich habe zu viel Angst, dass jemand sagt: "Aber warum hast du es so langsam und beschissen geschrieben?" so kann ich sie nie richtig auf mich selbst anwenden.
Ich bin allerdings nicht so schlimm, wie ich es hier sehe. Und ich bin völlig immun gegen Selbsttäuschung, also weißt du, dass ich damit Recht habe!
BEARBEITEN
Ich sollte hinzufügen - einer meiner größten dummen Probleme ist der Aufwand für Anrufe. Natürlich nicht überall, aber in jenen Fällen, in denen ich denke, dass es sehr oft in inneren Schleifen verwendet wird (in denen ich keine objektiven Beweise für ein Problem habe). Ich habe bösen offsetof-basierten Code geschrieben, um zu vermeiden, dass virtuelle Methodenaufrufe mehrmals ausgeführt werden. Das Ergebnis ist möglicherweise sogar langsamer, da es wahrscheinlich kein Codierungsstil ist, mit dem Optimierer gut umgehen sollen - aber es ist schlauer ;-)
quelle
In den alten Zeiten, in denen auf Computern die Uhrzeit in Mikrosekunden, der Speicher in Kilobyte und die primitiven Compiler gemessen wurden, ergab die Mikrooptimierung einen Sinn.
Aufgrund der Geschwindigkeit und Größe der Computer der aktuellen Generation sowie der Qualität der Compiler der aktuellen Generation ist die Mikrooptimierung in der Regel eine reine Zeitverschwendung. Ausnahmen sind in der Regel, wenn Sie aus einem rechenintensiven Code unbedingt die maximale Leistung herausholen müssen ... oder wenn Sie für ein eingebettetes System mit äußerst begrenzten Ressourcen schreiben.
Twitter und Facebook fallen mir ein :-)
quelle