it-swarm.com.de

Ist es in Ordnung, optimierten Code durch lesbaren Code zu ersetzen?

Manchmal stoßen Sie auf eine Situation, in der Sie vorhandenen Code erweitern/verbessern müssen. Sie sehen, dass der alte Code sehr schlank ist, aber auch schwer zu erweitern ist und Zeit zum Lesen benötigt.

Ist es eine gute Idee, es durch modernen Code zu ersetzen?

Vor einiger Zeit mochte ich den Lean-Ansatz, aber jetzt scheint es mir besser, viele Optimierungen zugunsten höherer Abstraktionen, besserer Schnittstellen und besser lesbaren, erweiterbaren Codes zu opfern.

Die Compiler scheinen ebenfalls besser zu werden, so dass Dinge wie struct abc = {} Im Stillen in memsets umgewandelt werden. shared_ptr S produzieren so ziemlich den gleichen Code wie Rohzeiger-Twiddling-Vorlagen arbeiten super gut, weil sie super schlanken Code produzieren und so weiter.

Trotzdem sieht man manchmal stapelbasierte Arrays und alte C-Funktionen mit einer obskuren Logik, und normalerweise befinden sie sich nicht auf dem kritischen Pfad.

Ist es eine gute Idee, einen solchen Code zu ändern, wenn Sie ein kleines Stück davon so oder so berühren müssen?

79
Coder

Wo?

  • Auf einer Homepage einer Google-Website ist dies nicht akzeptabel. Halte die Dinge so schnell wie möglich.

  • In einem Teil einer Anwendung, der einmal im Jahr von einer Person verwendet wird, ist es durchaus akzeptabel, die Leistung zu opfern, um die Lesbarkeit des Codes zu verbessern.

Im Allgemeinen was sind die nicht funktionalen Anforderungen für den Teil des Codes, an dem Sie arbeiten? Wenn eine Aktion unter 900 ms ausgeführt werden muss. In einem bestimmten Kontext (Maschine, Last usw.) wird in 80% der Fälle eine Leistung von weniger als 200 ms erzielt. Machen Sie den Code in 100% der Fälle besser lesbar, auch wenn dies die Leistung geringfügig beeinträchtigen könnte. Wenn andererseits dieselbe Aktion nie unter zehn Sekunden ausgeführt wurde, sollten Sie lieber versuchen, herauszufinden, was mit der Leistung (oder der Anforderung überhaupt) nicht stimmt.

Außerdem wie eine Verbesserung der Lesbarkeit die Leistung verringert? Häufig passen Entwickler das Verhalten an eine vorzeitige Optimierung an: Sie haben Angst, die Lesbarkeit zu verbessern, da sie glauben, dass dies die Leistung drastisch beeinträchtigt, während sie besser lesbar ist Code wird einige Mikrosekunden länger damit verbringen, dieselbe Aktion auszuführen.

115

Normalerweise nein.

Das Ändern des Codes kann zu unvorhergesehenen Problemen an anderer Stelle im System führen (die manchmal bis viel später in einem Projekt unbemerkt bleiben, wenn keine festen Einheiten- und Rauchtests vorhanden sind). Normalerweise gehe ich an der Mentalität "Wenn es nicht kaputt ist, repariere es nicht" vorbei.

Die Ausnahme von dieser Regel besteht darin, dass Sie eine neue Funktion implementieren, die diesen Code berührt. Wenn dies zu diesem Zeitpunkt keinen Sinn ergibt und das Refactoring wirklich stattfinden muss, sollten Sie es durchführen, solange die Refactoring-Zeit (und ausreichende Tests und Puffer für die Behandlung von Folgeschäden) in Schätzungen berücksichtigt werden.

Natürlich Profil, Profil, Profil , besonders wenn es sich um einen kritischen Pfadbereich handelt.

36
Demian Brecht

Kurz gesagt: es kommt darauf an

  • Benötigen oder verwenden Sie wirklich Ihre überarbeitete/erweiterte Version?

    • Gibt es einen konkreten unmittelbaren oder langfristigen Gewinn?
    • Ist dieser Gewinn nur für die Wartbarkeit oder wirklich architektonisch?
  • Muss es wirklich optimiert werden?

    • Warum?
    • Welchen Zielgewinn müssen Sie anstreben?

Im Detail

Wirst du das aufgeräumte, glänzende Zeug brauchen?

Hier gibt es einige Dinge, bei denen Sie vorsichtig sein müssen, und Sie müssen die Grenze zwischen dem tatsächlichen, messbaren Gewinn und Ihrer persönlichen Präferenz und der potenziellen schlechten Angewohnheit, Code zu berühren, die nicht sein sollte, ermitteln.

Genauer gesagt, wissen Sie Folgendes:

Es gibt so etwas wie Over-Engineering

Es ist ein Anti-Pattern und enthält einige integrierte Probleme:

  • es ist möglicherweise erweiterbarer , aber es ist möglicherweise nicht einfacher zu erweitern ,
  • es ist möglicherweise nicht einfacher zu verstehen ,
  • last, but not least hier: Sie könnten den gesamten Code verlangsamen.

Einige könnten auch das KISS-Prinzip als Referenz erwähnen, aber hier ist es kontraintuitiv: Ist der optimierte Weg der einfache Weg oder der saubere architektonische Weg? Die Antwort ist nicht unbedingt absolut, wie im Rest unten erläutert.

Du wirst es nicht brauchen

Das YAGNI-Prinzip ist nicht vollständig orthogonal zum anderen Thema, aber es hilft, sich die Frage zu stellen: Wirst du es brauchen?

Bietet die komplexere Architektur wirklich einen Vorteil für Sie, abgesehen davon, dass sie den Eindruck einer wartungsfreundlicheren Architektur erweckt?

Wenn es nicht kaputt ist, reparieren Sie es nicht

Schreiben Sie dies auf ein großes Poster und hängen Sie es neben Ihren Bildschirm oder in den Küchenbereich bei der Arbeit oder in den Besprechungsraum des Entwicklers. Natürlich gibt es viele andere Mantras, die es wert sind, wiederholt zu werden, aber dieses ist wichtig, wenn Sie versuchen, "Wartungsarbeiten" durchzuführen und den Drang verspüren, es zu "verbessern".

Es ist für uns selbstverständlich, dass wir den Code "verbessern" oder sogar nur unbewusst berühren wollen, während wir ihn durchlesen, um zu versuchen, ihn zu verstehen. Es ist eine gute Sache, da es bedeutet, dass wir eine Meinung haben und versuchen, ein tieferes Verständnis der Interna zu erlangen, aber es hängt auch von unserem Können, unserem Wissen ab (wie entscheiden Sie, was besser ist oder nicht? Nun, siehe Abschnitte unten ...) und alle Annahmen, die wir über das machen, was wir zu kennen glauben ...:

  • tut es tatsächlich,
  • muss eigentlich tun,
  • wird irgendwann tun müssen,
  • und wie gut es geht.

Muss es wirklich optimiert werden?

All dies sagte, warum wurde es überhaupt "optimiert"? Sie sagen, dass vorzeitige Optimierung die Wurzel allen Übels ist, und wenn Sie undokumentierten und scheinbar optimierten Code sehen, können Sie normalerweise annehmen, dass er wahrscheinlich nicht den Regeln der Optimierung entspricht Ich brauchte den Optimierungsaufwand nicht unbedingt und es war nur die übliche Hybris des Entwicklers. Noch einmal, vielleicht ist es nur Ihre, die jetzt spricht.

Wenn ja, innerhalb welcher Grenzen wird es akzeptabel? Wenn es nötig ist, besteht diese Grenze und gibt Ihnen Raum, um Dinge zu verbessern, oder eine harte Linie, um zu entscheiden, sie loszulassen.

Achten Sie auch auf unsichtbare Eigenschaften. Möglicherweise wird Ihre "erweiterbare" Version dieses Codes auch zur Laufzeit mehr Speicherplatz beanspruchen und sogar einen größeren statischen Speicherbedarf für die ausführbare Datei bieten. Shiny OO Funktionen sind mit solchen nicht intuitiven Kosten verbunden und können für Ihr Programm und die Umgebung, in der es ausgeführt werden soll, von Bedeutung sein.

Messen, messen, messen

Wie die Google-Leute jetzt dreht sich alles um Daten! Wenn Sie es mit Daten sichern können, ist es notwendig.

Es gibt diese nicht so alte Geschichte, dass auf jeden in der Entwicklung ausgegebenen Dollar 1 mindestens 1 Dollar beim Testen und folgen ) mindestens $ 1 an Unterstützung (aber wirklich, es ist viel mehr).

Veränderungen wirken sich auf viele Dinge aus:

  • möglicherweise müssen Sie einen neuen Build erstellen.
  • sie sollten neue Komponententests schreiben (definitiv, wenn es keine gab, und Ihre erweiterbarere Architektur lässt wahrscheinlich Platz für mehr, da Sie mehr Oberfläche für Fehler haben).
  • sie sollten neue Leistungstests schreiben (um sicherzustellen, dass dies auch in Zukunft stabil bleibt und wo die Engpässe liegen), und diese sind schwierig durchzuführen ;;
  • sie müssen es dokumentieren (und erweiterbarer bedeutet mehr Raum für Details).
  • sie (oder jemand anderes) müssen es in der Qualitätssicherung ausgiebig erneut testen.
  • code ist (fast) nie fehlerfrei und muss unterstützt werden.

Es ist also nicht nur der Verbrauch von Hardwareressourcen (Ausführungsgeschwindigkeit oder Speicherbedarf), den Sie hier messen müssen, sondern auch Verbrauch von Teamressourcen . Beide müssen vorhergesagt werden, um ein Ziel zu definieren, gemessen, berücksichtigt und basierend auf der Entwicklung angepasst werden.

Und für Ihren Manager bedeutet dies, dass Sie ihn in den aktuellen Entwicklungsplan integrieren. Kommunizieren Sie also darüber und lassen Sie sich nicht auf die Codierung von wütenden Cowboys, U-Booten und Black-Ops ein.


Allgemein...

Ja aber...

Versteh mich nicht falsch, im Allgemeinen würde ich dafür sein, warum du es vorschlägst, und ich befürworte es oft. Sie müssen sich jedoch der langfristigen Kosten bewusst sein.

In einer perfekten Welt ist es die richtige Lösung:

  • Computerhardware mit der Zeit besser werden,
  • compiler und Laufzeitplattformen werden mit der Zeit besser.
  • sie erhalten nahezu perfekten, sauberen, wartbaren und lesbaren Code.

In der Praxis:

  • sie können es noch schlimmer machen

    Sie brauchen mehr Augäpfel, um es zu betrachten, und je komplexer Sie es machen, desto mehr Augäpfel benötigen Sie.

  • sie können die Zukunft nicht vorhersagen

    Sie können nicht mit absoluter Sicherheit wissen, ob Sie es jemals brauchen werden und nicht einmal, ob die "Erweiterungen", die Sie benötigen, einfacher und schneller in der alten Form zu implementieren gewesen wären und ob sie selbst superoptimiert werden müssten .

  • aus Sicht des Managements bedeutet dies enorme Kosten ohne direkten Gewinn.

Machen Sie es Teil des Prozesses

Sie erwähnen hier, dass es sich um eine eher kleine Änderung handelt und Sie einige spezifische Probleme im Auge haben. Ich würde sagen, dass es in diesem Fall normalerweise in Ordnung ist, aber die meisten von uns haben auch persönliche Geschichten über kleine Änderungen, fast chirurgische Eingriffe, die sich schließlich in einen Wartungsalptraum verwandelten und Termine fast verpassten oder explodierten, weil Joe Programmer keine sah der Gründe hinter dem Code und berührte etwas, das nicht hätte sein sollen.

Wenn Sie einen Prozess haben, um solche Entscheidungen zu handhaben, nehmen Sie ihnen den persönlichen Vorteil:

  • Wenn Sie die Dinge richtig testen, wissen Sie schneller, ob die Dinge kaputt sind.
  • Wenn Sie sie messen, wissen Sie, ob sie sich verbessert haben.
  • Wenn Sie es überprüfen, werden Sie wissen, ob es Leute abschreckt.

Testabdeckung, Profilerstellung und Datenerfassung sind schwierig

Aber natürlich können Ihr Testcode und Ihre Testmetriken unter denselben Problemen leiden, die Sie für Ihren eigentlichen Code vermeiden möchten: Testen Sie die richtigen Dinge und sind sie das Richtige für die Zukunft und messen Sie die richtigen Dinge?

Im Allgemeinen gilt: Je mehr Sie testen (bis zu einem bestimmten Grenzwert) und messen, desto mehr Daten erfassen Sie und desto sicherer sind Sie. Schlechte Analogiezeit: Stellen Sie sich das wie Fahren vor (oder das Leben im Allgemeinen): Sie können der beste Fahrer der Welt sein, wenn das Auto bei Ihnen kaputt geht oder jemand beschlossen hat, sich selbst zu töten, indem er heute mit seinem eigenen Auto in Ihr Auto fährt Fähigkeiten könnten nicht genug sein. Es gibt sowohl Umweltaspekte, die Sie treffen können, als auch menschliche Fehler.

Code Reviews sind die Flurtests des Entwicklungsteams

Und ich denke, der letzte Teil ist hier der Schlüssel: Codeüberprüfungen durchführen. Sie werden den Wert Ihrer Verbesserungen nicht kennen, wenn Sie sie alleine machen. Codeüberprüfungen sind unsere "Flurtests": Folgen Sie Raymond's Version des Linus'schen Gesetzes sowohl zum Erkennen von Fehlern als auch zum Erkennen von Überentwicklung und anderen Anti-Mustern und um sicherzustellen, dass der Code mit Ihrem übereinstimmt Fähigkeiten des Teams. Es macht keinen Sinn, den "besten" Code zu haben, wenn niemand anderes als Sie ihn verstehen und pflegen kann. Dies gilt sowohl für kryptische Optimierungen als auch für 6-Ebenen-Architekturentwürfe.

Denken Sie als abschließende Worte daran:

Jeder weiß, dass das Debuggen doppelt so schwierig ist wie das Schreiben eines Programms. Also, wenn Sie so klug sind, wie Sie sein können, wenn Sie es schreiben, wie werden Sie es jemals debuggen? - Brian Kernighan

29
haylem

Im Allgemeinen sollten Sie sich zuerst auf die Lesbarkeit und viel später auf die Leistung konzentrieren. Meistens sind diese Leistungsoptimierungen ohnehin vernachlässigbar, aber die Wartungskosten können enorm sein.

Sicherlich sollten alle "kleinen" Dinge zugunsten der Klarheit geändert werden, da, wie Sie betont haben, die meisten davon sowieso vom Compiler optimiert werden.

Bei den größeren Optimierungen besteht möglicherweise die Möglichkeit, dass die Optimierungen tatsächlich entscheidend für das Erreichen einer angemessenen Leistung sind (obwohl dies nicht überraschend oft der Fall ist). Ich würde Ihre Änderungen vornehmen und dann den Code vor und nach den Änderungen profilieren. Wenn der neue Code erhebliche Leistungsprobleme aufweist, können Sie jederzeit auf die optimierte Version zurücksetzen. Wenn nicht, können Sie sich einfach an die sauberere Codeversion halten.

Ändern Sie jeweils nur einen Teil des Codes und prüfen Sie, wie sich dies nach jeder Refactoring-Runde auf die Leistung auswirkt.

8
Oleksi

Dies hängt davon ab, warum der Code optimiert wurde und welche Auswirkungen eine Änderung haben würde und welche Auswirkungen der Code auf die Gesamtleistung haben könnte. Es sollte auch davon abhängen, ob Sie eine gute Möglichkeit haben, Teständerungen zu laden.

Sie sollten diese Änderung nicht vornehmen, ohne vorher und nachher und vorzugsweise unter einer Last, die der Produktion ähnelt, ein Profil zu erstellen. Das bedeutet, dass Sie keine winzige Teilmenge von Daten auf einem Entwicklercomputer verwenden oder testen müssen, wenn nur ein Benutzer das System verwendet.

Wenn die Optimierung kürzlich durchgeführt wurde, können Sie möglicherweise mit dem Entwickler sprechen und herausfinden, was genau das Problem war und wie langsam die Anwendung vor der Optimierung war. Dies kann Ihnen viel darüber sagen, ob es sich lohnt, die Optimierung durchzuführen, und für welche Bedingungen die Optimierung erforderlich war (ein Bericht, der beispielsweise ein ganzes Jahr abdeckt, ist möglicherweise erst im September oder Oktober langsam geworden, wenn Sie Ihre Änderung testen im Februar ist die Langsamkeit möglicherweise noch nicht erkennbar und der Test ungültig).

Wenn die Optimierung ziemlich alt ist, sind neuere Methoden möglicherweise sogar schneller und besser lesbar.

Letztendlich ist dies eine Frage an Ihren Chef. Es ist zeitaufwändig, etwas zu überarbeiten, das optimiert wurde, und sicherzustellen, dass die Änderung das Endergebnis nicht beeinflusst und dass es im Vergleich zur alten Methode genauso gut oder zumindest akzeptabel funktioniert. Er möchte möglicherweise, dass Sie Ihre Zeit in anderen Bereichen verbringen, anstatt eine Aufgabe mit hohem Risiko zu übernehmen, um ein paar Minuten Codierungszeit zu sparen. Oder er stimmt zu, dass der Code schwer zu verstehen ist und häufig eingegriffen werden muss und dass jetzt bessere Methoden verfügbar sind.

8
HLGEM

wenn Profiling anzeigt, dass die Optimierung nicht erforderlich ist (sie befindet sich nicht in einem kritischen Abschnitt) oder sogar eine schlechtere Laufzeit hat (aufgrund einer schlechten vorzeitigen Optimierung), ersetzen Sie sie auf jeden Fall durch den lesbaren Code, der einfacher zu verwenden ist pflegen

stellen Sie außerdem sicher, dass sich der Code bei den entsprechenden Tests gleich verhält

6
ratchet freak

Betrachten Sie es aus geschäftlicher Sicht. Was kostet die Änderung? Wie viel Zeit benötigen Sie, um die Änderung vorzunehmen, und wie viel sparen Sie auf lange Sicht, wenn Sie den Code einfacher erweitern oder warten lassen? Fügen Sie dieser Zeit nun ein Preisschild hinzu und vergleichen Sie es mit dem Geld, das durch Leistungsreduzierung verloren gegangen ist. Möglicherweise müssen Sie einen Server hinzufügen oder aktualisieren, um den Leistungsverlust auszugleichen. Möglicherweise entspricht das Produkt nicht mehr den Anforderungen und kann nicht mehr verkauft werden. Vielleicht gibt es keinen Verlust. Möglicherweise erhöht die Änderung die Robustheit und spart an anderer Stelle Zeit. Treffen Sie jetzt Ihre Entscheidung.

Nebenbei bemerkt, in einigen Fällen ist es möglicherweise möglich, beide Versionen eines Fragments beizubehalten. Sie können einen Test schreiben, der zufällige Eingabewerte generiert, und die Ergebnisse mit der anderen Version überprüfen. Verwenden Sie die "clevere" Lösung, um das Ergebnis einer vollkommen verständlichen und offensichtlich korrekten Lösung zu überprüfen und dadurch die Gewissheit (aber keinen Beweis) zu gewinnen, dass die neue Lösung der alten entspricht. Oder gehen Sie in die andere Richtung und überprüfen Sie das Ergebnis des kniffligen Codes mit dem ausführlichen Code, um die Absicht hinter dem Hack eindeutig zu dokumentieren.

5
scarfridge

Grundsätzlich fragen Sie, ob Refactoring ein lohnendes Unterfangen ist. Die Antwort darauf lautet mit Sicherheit ja.

Aber...

... Sie müssen es sorgfältig tun. Für jeden Code, den Sie umgestalten, benötigen Sie solide Unit-, Integrations-, Funktions- und Leistungstests. Sie müssen sicher sein, dass sie wirklich alle erforderlichen Funktionen testen. Sie benötigen die Fähigkeit, sie einfach und wiederholt auszuführen. Sobald Sie das haben, sollten Sie in der Lage sein, Komponenten durch neue Komponenten zu ersetzen, die die entsprechende Funktionalität enthalten.

Martin Fowler hat das Buch dazu geschrieben.

4
Matthew Flynn

Die Antwort lautet ohne Verlust der Allgemeinheit ja. Fügen Sie immer modernen Code hinzu, wenn Sie schwer lesbaren Code sehen, und löschen Sie den fehlerhaften Code in den meisten Fällen. Ich benutze den folgenden Prozess:

  1. Suchen Sie nach dem Leistungstest und den unterstützenden Profilinformationen. Wenn es keinen Leistungstest gibt, kann das, was ohne Beweise behauptet werden kann, ohne Beweise abgewiesen werden. Stellen Sie sicher, dass Ihr moderner Code schneller ist, und entfernen Sie den alten Code. Wenn jemand argumentiert (auch Sie selbst), bitten Sie ihn, den Profilierungscode zu schreiben, um zu beweisen, welcher schneller ist.
  2. Wenn der Profiling-Code vorhanden ist, schreiben Sie den modernen Code trotzdem. Nennen Sie es so etwas wie <function>_clean(). Dann "rennen" Sie Ihren Code gegen den schlechten Code. Wenn Ihr Code besser ist, entfernen Sie den alten Code.
  3. Wenn der alte Code schneller ist, lassen Sie Ihren modernen Code trotzdem dort. Es dient als gute Dokumentation für die Aufgaben des anderen Codes. Da der "Race" -Code vorhanden ist, können Sie ihn weiter ausführen, um die Leistungsmerkmale und Unterschiede zwischen den beiden Pfaden zu dokumentieren. Sie können auch Unit-Tests auf Unterschiede im Code-Verhalten durchführen. Wichtig ist, dass der moderne Code eines Tages garantiert den "optimierten" Code übertrifft. Sie können dann den fehlerhaften Code entfernen.

QED.

3
Sunny Kalsi

Sie sollten den funktionierenden Produktionscode nicht ohne guten Grund ändern. "Refactoring" ist kein guter Grund, es sei denn, Sie können Ihre Arbeit ohne dieses Refactoring erledigen. Selbst wenn Sie Fehler im schwierigen Code selbst beheben, sollten Sie sich die Zeit nehmen, ihn zu verstehen und die kleinstmögliche Änderung vorzunehmen. Wenn der Code so schwer zu verstehen ist, können Sie ihn nicht vollständig verstehen. Daher haben alle Änderungen, die Sie vornehmen, unvorhersehbare Nebenwirkungen - mit anderen Worten: Fehler. Je größer die Änderung ist, desto wahrscheinlicher ist es, dass Sie Probleme verursachen.

Es würde eine Ausnahme geben: Wenn der unverständliche Code einen vollständigen Satz von Komponententests hätte, könnten Sie ihn umgestalten. Da ich noch nie einen unverständlichen Code mit vollständigen Komponententests gesehen oder gehört habe, schreiben Sie zuerst die Komponententests, holen die Zustimmung der erforderlichen Personen ein, dass diese Komponententests tatsächlich das darstellen, was der Code tun soll, und nehmen dann den Code vor . Ich habe das ein- oder zweimal gemacht; Es ist ein Schmerz im Nacken und sehr teuer, führt aber am Ende zu guten Ergebnissen.

3
mjfgates

Wenn es sich nur um einen kurzen Code handelt, der etwas relativ Einfaches auf schwer verständliche Weise erledigt, würde ich das "schnelle Verständnis" in einem erweiterten Kommentar und/oder einer nicht verwendeten alternativen Implementierung wie verschieben

#ifdef READABLE_ALT_IMPLEMENTATION

   double x=0;
   for(double n: summands)
     x += n;
   return x;

#else

   auto subsum = [&](int lb, int rb){
          double x=0;
          while(lb<rb)
            x += summands[lb++];
          return x;
        };
   double x_fin=0;
   for(double nsm: par_eval( subsum
                           , partitions(n_threads, 0, summands.size()) ) )
     x_fin += nsm;
   return x_fin;

#endif
3
leftaroundabout

Wenn ich der Welt eine Sache (über Software) beibringen könnte, bevor ich sterbe, würde ich ihr beibringen, dass "Leistung gegen X" ein falsches Dilemma ist.

Refactoring ist in der Regel als Segen für Lesbarkeit und Zuverlässigkeit bekannt, kann aber ebenso einfach die Optimierung unterstützen. Wenn Sie die Leistungsverbesserung als eine Reihe von Refactorings behandeln, können Sie die Campsite-Regel einhalten und gleichzeitig die Anwendung beschleunigen. Zumindest meiner Meinung nach obliegt es Ihnen tatsächlich, dies zu tun.

Zum Beispiel ist der Autor dieser Frage auf einen verrückten Code gestoßen. Wenn diese Person meinen Code lesen würde, würde sie feststellen, dass der verrückte Teil 3-4 Zeilen lang ist. Es befindet sich in einer Methode für sich und der Methodenname und die Beschreibung geben an, WAS die Methode tut. Die Methode würde 2-6 Zeilen Inline-Kommentare enthalten, die beschreiben, wie der verrückte Code trotz seines fragwürdigen Erscheinungsbilds die richtige Antwort erhält.

Auf diese Weise unterteilt, können Sie Implementierungen dieser Methode nach Belieben austauschen. In der Tat habe ich wahrscheinlich so die verrückte Version geschrieben. Sie können es gerne versuchen oder zumindest nach Alternativen fragen. Die meiste Zeit werden Sie feststellen, dass die naive Implementierung merklich schlechter ist (normalerweise kümmere ich mich nur um eine 2-10-fache Verbesserung), aber Compiler und Bibliotheken ändern sich ständig, und wer weiß, was Sie heute möglicherweise finden, das wann nicht verfügbar war Die Funktion wurde geschrieben?

3
Jason

Es ist wahrscheinlich keine gute Idee, ihn zu berühren. Wenn der Code aus Leistungsgründen so geschrieben wurde, bedeutet dies, dass eine Änderung zu Problemen führen kann, die zuvor behoben wurden.

Wenn Sie tun entscheiden, die Dinge so zu ändern, dass sie besser lesbar und erweiterbar sind: Bevor Sie eine Änderung vornehmen, vergleichen Sie den alten Code unter schwere Last. Noch besser, wenn Sie ein altes Dokument oder ein Trouble Ticket finden, das das Leistungsproblem beschreibt, das dieser seltsam aussehende Code beheben soll. Nachdem Sie Ihre Änderungen vorgenommen haben, führen Sie die Leistungstests erneut aus. Wenn es nicht sehr unterschiedlich ist oder immer noch innerhalb akzeptabler Parameter liegt, ist es wahrscheinlich in Ordnung.

Es kann manchmal vorkommen, dass dieser leistungsoptimierte Code, wenn sich andere Teile eines Systems ändern, keine so umfangreichen Optimierungen mehr benötigt, aber ohne strenge Tests ist dies nicht sicher zu erkennen.

Das Problem hierbei ist die Unterscheidung zwischen "optimiert" und lesbar und erweiterbar. Was wir als Benutzer als optimierten Code sehen und was der Compiler als optimiert ansieht, sind zwei verschiedene Dinge. Der Code, den Sie ändern möchten, ist möglicherweise überhaupt kein Engpass. Selbst wenn der Code "schlank" ist, muss er nicht einmal "optimiert" werden. Wenn der Code alt genug ist, kann der Compiler Optimierungen an integrierten Funktionen vornehmen, die die Verwendung einer neueren einfachen integrierten Struktur genauso oder effizienter machen als der alte Code.

Und "schlanker", nicht lesbarer Code wird nicht immer optimiert.

Früher war ich der Meinung, dass cleverer/schlanker Code guter Code ist, aber manchmal hat es mich bei jeder eingebetteten Arbeit gebissen, wenn ich versucht habe, obskure Regeln der Sprache auszunutzen, anstatt bei der Codeerstellung zu helfen Seien Sie schlau, denn der Compiler macht Ihren klugen Code zu etwas, das von der eingebetteten Hardware völlig unbrauchbar wird.

2
Jeff Langemeier

Ich werde optimierten Code niemals durch lesbaren Code ersetzen, da ich bei der Leistung keine Kompromisse eingehen kann, und ich werde mich dafür entscheiden, in jedem Abschnitt die richtigen Kommentare zu verwenden, damit jeder die in diesem optimierten Abschnitt implementierte Logik verstehen kann, die beide Probleme löst.

Daher wird der Code optimiert und durch richtiges Kommentieren wird er auch lesbar.

HINWEIS: Sie können einen optimierten Code mithilfe korrekter Kommentare lesbar machen, aber Sie können lesbaren Code nicht zu einem optimierten Code machen.

2
Logicalj

Hier ist ein Beispiel, um den Unterschied zwischen einfachem Code und optimiertem Code zu sehen: https://stackoverflow.com/a/11227902/1396264

gegen Ende der Antwort ersetzt er nur:

if (data[c] >= 128)
    sum += data[c];

mit:

int t = (data[c] - 128) >> 31;
sum += ~t & data[c];

Um fair zu sein, ich habe keine Ahnung, durch was die if-Anweisung ersetzt wurde, aber wie der Antwortende sagt, sind es einige bitweise Operationen, die das gleiche Ergebnis liefern (ich nehme nur sein Wort dafür) .

Dies wird in weniger als einem Viertel der ursprünglichen Zeit ausgeführt (11,54 Sekunden gegenüber 2,5 Sekunden).

2
vedant

Die Hauptfrage hier ist: Ist die Optimierung erforderlich?

Wenn dies der Fall ist, können Sie es nicht durch langsameren, besser lesbaren Code ersetzen. Sie müssen Kommentare usw. hinzufügen, um die Lesbarkeit zu verbessern.

Wenn der Code nicht optimiert werden muss, sollte dies nicht der Fall sein (bis zur Beeinträchtigung der Lesbarkeit), und Sie können ihn neu faktorisieren, um ihn besser lesbar zu machen.

JEDOCH - Stellen Sie sicher, dass Sie genau wissen, was der Code tut und wie Sie ihn gründlich testen können, bevor Sie Änderungen vornehmen. Dies schließt die Spitzenauslastung usw. ein. Wenn Sie keine Testfälle erstellen und diese vorher und nachher ausführen müssen, haben Sie keine Zeit für das Refactoring.

1
Stefan

So mache ich Dinge: Zuerst lasse ich es in lesbarem Code funktionieren, dann optimiere ich es. Ich behalte die Originalquelle und dokumentiere meine Optimierungsschritte.

Wenn ich dann eine Funktion hinzufügen muss, kehre ich zu meinem lesbaren Code zurück, füge die Funktion hinzu und folge den von mir dokumentierten Optimierungsschritten. Da Sie dokumentiert haben, ist es sehr schnell und einfach, Ihren Code mit der neuen Funktion erneut zu optimieren.

1
Pieter B

Optimierung ist relativ. Zum Beispiel:

Stellen Sie sich eine Klasse mit einer Reihe von BOOL-Mitgliedern vor:

// no nitpicking over BOOL vs bool allowed
class Pear {
 ...
 BOOL m_peeled;
 BOOL m_sliced;
 BOOL m_pitted;
 BOOL m_rotten;
 ...
};

Sie könnten versucht sein, die BOOL-Felder in Bitfelder zu konvertieren:

class Pear {
 ...
 BOOL m_peeled:1;
 BOOL m_sliced:1;
 BOOL m_pitted:1;
 BOOL m_rotten:1;
 ...
};

Da ein BOOL als INT typisiert ist (was auf Windows-Plattformen eine vorzeichenbehaftete 32-Bit-Ganzzahl ist), benötigt dies 16 Bytes und packt sie in eines. Das sind 93% Ersparnis! Wer könnte sich darüber beschweren?

Diese Annahme:

Da ein BOOL als INT typisiert ist (was auf Windows-Plattformen eine vorzeichenbehaftete 32-Bit-Ganzzahl ist), benötigt dies 16 Bytes und packt sie in eines. Das sind 93% Ersparnis! Wer könnte sich darüber beschweren?

führt zu:

Das Konvertieren eines BOOL in ein Einzelbitfeld spart drei Byte Daten, kostet Sie jedoch acht Byte Code, wenn dem Mitglied ein nicht konstanter Wert zugewiesen wird. Ebenso wird das Extrahieren des Wertes teurer.

Was früher war

 Push [ebx+01Ch]      ; m_sliced
 call [email protected]    ; Something(m_sliced);

wird

 mov  ecx, [ebx+01Ch] ; load bitfield value
 shl  ecx, 30         ; put bit at top
 sar  ecx, 31         ; move down and sign extend
 Push ecx
 call [email protected]    ; Something(m_sliced);

Die Bitfeldversion ist um neun Bytes größer.

Setzen wir uns und rechnen. Angenommen, auf jedes dieser Bitfeldfelder wird in Ihrem Code sechsmal zugegriffen, dreimal zum Schreiben und dreimal zum Lesen. Die Kosten für das Codewachstum betragen ungefähr 100 Byte. Es werden nicht genau 102 Bytes sein, da der Optimierer möglicherweise in der Lage ist, Werte zu nutzen, die bereits in Registern für einige Operationen vorhanden sind, und die zusätzlichen Anweisungen möglicherweise versteckte Kosten in Bezug auf eine verringerte Registerflexibilität verursachen. Der tatsächliche Unterschied kann größer sein, er kann geringer sein, aber für eine Berechnung auf der Rückseite des Umschlags nennen wir ihn 100. In der Zwischenzeit betrug die Speicherersparnis 15 Byte pro Klasse. Daher beträgt die Gewinnschwelle sieben. Wenn Ihr Programm weniger als sieben Instanzen dieser Klasse erstellt, übersteigen die Codekosten die Dateneinsparungen: Ihre Speicheroptimierung war eine Speicherentoptimierung.

Referenzen

0
Paul Sweatte

IMHO-Lesbarkeit ist wichtiger als optimierter Code, da sich die Mikrooptimierung in den meisten Fällen nicht lohnt.

Artikel über nsinnige Mikrooptimierungen :

Wie die meisten von uns bin ich es leid, Blog-Beiträge über unsinnige Mikrooptimierungen wie das Ersetzen von Druck durch Echo, ++ $ i durch $ i ++ oder doppelte Anführungszeichen durch einfache Anführungszeichen zu lesen. Warum? Da 99,999999% der Zeit, ist es irrelevant.

"print" verwendet einen Opcode mehr als "echo", weil es tatsächlich etwas zurückgibt. Wir können daraus schließen, dass das Echo schneller ist als das Drucken. Aber ein Opcode kostet nichts, wirklich nichts.

Ich habe eine Neuinstallation versucht WordPress. Das Skript wird angehalten, bevor es mit einem "Busfehler" auf meinem Laptop endet, aber die Anzahl der Opcodes lag bereits bei mehr als 2,3 Millionen. Genug gesagt.

0
webvitaly