Warum sollte ich mich für die Leistung und Effizienz des Mikros interessieren?


71

In vielen Fragen und Antworten auf den C / C ++ - Seiten werden speziell oder indirekt Probleme mit der Leistung von Mikros (z. B. der Overhead einer indirekten vs direkten vs Inline-Funktion) oder die Verwendung eines O (N 2 ) vs O (N log N) -Algorithmus behandelt eine Liste mit 100 Artikeln.

Ich programmiere immer ohne Rücksicht auf die Leistung der Mikros und ohne Rücksicht auf die Leistung der Makros, wobei ich mich auf einfach zu wartenden, zuverlässigen Code konzentriere, es sei denn oder bis ich weiß, dass ich ein Problem habe.

Meine Frage ist, warum es so viele Programmierer interessiert? Ist es wirklich ein Problem für die meisten Entwickler? Habe ich das Glück gehabt, dass ich mir keine allzu großen Sorgen machen musste, oder bin ich ein schlechter Programmierer?


5
+1, gute allgemeine Frage.
iammilind

+1 gute Frage .. Ich habe 2 Tags hinzugefügt .. hoffe, es macht Ihnen nichts aus.

2
Ich führe zwei großartige Zitate an: 1) "Vorzeitige Optimierung ist die Wurzel allen Übels." 2) 80% Ihrer Zeit werden mit 20% Ihres Codes verbracht (80/20 Regel).
James Khoury

2
Ich bemerke ein paar Antworten, die über mein O (n * n) -Beispiel sprechen. Ich habe explizit eine Liste mit 100 Elementen angegeben, sie bestehen jedoch weiterhin darauf, dass O (nlogn) besser ist, und geben explizit Leistungsverbesserungen an, wenn die Liste in der Zukunft auf 1000er oder Millionen geht. Ist diese Mikrooptimierung besessen, weil Programmierer auf mögliche zukünftige Anforderungen programmieren, eher auf aktuelle Anforderungen? (Wo habe ich das schon
mal

5
@James das vollständige Zitat von Donald Knuth lautet: "Wir sollten kleine Wirkungsgrade vergessen, sagen wir in 97% der Fälle: Vorzeitige Optimierung ist die Wurzel allen Übels." Über die verbleibenden 3% in diesem Thread werden einige gute Antworten gegeben.
StuperUser

Antworten:


14

In der Praxis ist die Leistung selten ein Problem, das auf dieser Detailebene behandelt werden muss. Es lohnt sich, die Situation im Auge zu behalten, wenn Sie wissen, dass Sie große Datenmengen speichern und bearbeiten werden, aber ansonsten haben Sie Recht und es ist besser, wenn Sie die Dinge einfach halten.

Eine der einfachsten Fallen, in die man geraten kann - besonders in C und C ++, wo Sie eine so fein abgestimmte Steuerung haben - ist die zu frühe Optimierung auf einer zu feinen Ebene. Im Allgemeinen lautet die Regel: A) Optimieren Sie nicht, bis Sie herausfinden, dass Sie ein Problem haben, und B) optimieren Sie nichts, was Sie nicht als Problembereich erwiesen haben, mithilfe eines Profilers.

Eine Konsequenz zu B) ist: Programmierer können bekanntermaßen schlecht vorhersagen, wo ihre Leistungsengpässe sind, obwohl sie glauben, dass sie darin gut sind. Verwenden Sie einen Profiler, und optimieren Sie die langsamen Teile, oder ändern Sie die Algorithmen, wenn ein Codeabschnitt zu oft aufgerufen wird, sodass ein Problem auftritt.


6
Ein anderer: Initialisierungscode, der einmal ausgeführt wird, muss im Allgemeinen nicht optimiert werden.
Mike DeSimone

3
Kommt darauf an wie oft "einmal" ist. Beim Ausführen ./configurewürde ich sagen, dass in den Programmen, die das Skript ausführt, bis zu 75% der Laufzeit für "Initialisierungs" -Code aufgewendet werden. 25-50% könnten sogar für dynamische Verknüpfungen ausgegeben werden.
R ..

12
Regel A ist eine schreckliche Regel. Die Architektur eines Systems spielt eine Rolle für die Leistung, und wenn Sie später herausfinden, dass Ihre Architektur Ihre Leistungsanforderungen einfach nicht erfüllen kann, sind Sie im Grunde genommen durchgeknallt. Während Sie in der Lage sind, über feine Details hinwegzugehen, ist es einfach falsch, dies am Anfang vollständig zu ignorieren.
edA-qa mort-ora-y

3
@ edA-qa: Früher habe ich das gedacht, aber im Laufe der Jahre haben viele weitere Projekte Schwierigkeiten oder Misserfolge erlebt, bevor Leistungsüberlegungen Anlass zur Sorge gaben. Jedes Mal, wenn ich Leistungsprobleme hatte, war der Fix ein vergleichsweise kostengünstiger, tagelanger oder ein paarwöchiger Fehler. Kein Problem mehr als jeder andere "Bug", der einen Fehler in der Entwicklung entdeckte. Wie bei jedem anderen Risikoposten müssen jedoch Leistungsbeeinträchtigungen frühzeitig im Projekt identifiziert und gemindert werden.
Mattnz

5
Das OP fragte, warum es so wichtig sei, und ich verstehe nicht, wie diese Antwort die Frage tatsächlich beantwortete, es sei denn, das OP war mehr daran interessiert, jemanden sagen zu hören: "Mach dir darüber keine Sorgen!".
Red-Dirt

54

Ich denke, alles auf Ihrer Liste ist Mikrooptimierung, die im Allgemeinen nicht betrachtet werden sollte, mit Ausnahme von

Verwenden eines O (n * n) vs O (NlogN) -Algorithmus für eine 100-Artikel-Liste

was ich denke sollte angeschaut werden. Sicher, diese Liste umfasst derzeit 100 Elemente, und für kleine n ist alles schnell , aber ich würde wetten, dass derselbe Code für eine Liste mit mehreren Millionen Zeilen wiederverwendet wird und der Code weiterhin verwendet wird vernünftig arbeiten.

Die Wahl des richtigen Algorithmus ist niemals eine Mikrooptimierung. Sie wissen nie, welche Arten von Daten derselbe Code zwei Monate oder zwei Jahre später verwenden wird. Im Gegensatz zu den "Mikrooptimierungen", die mit Hilfe eines Profilers einfach anzuwenden sind, erfordern Algorithmusänderungen häufig eine erhebliche Neugestaltung, um die neuen Algorithmen effektiv zu nutzen. (Einige Algorithmen erfordern beispielsweise, dass die Eingabedaten bereits sortiert sind. Dies kann dazu führen, dass Sie wichtige Teile Ihrer Anwendungen ändern müssen, um sicherzustellen, dass die Daten sortiert bleiben.)


36
+1 für "Die Auswahl des richtigen Algorithmus ist niemals eine Mikrooptimierung."

9
Ich würde auch +1 geben, aber beachten Sie, dass die Auswahl des Big-O-optimalen Algorithmus bei kleinen Datenmengen die Entwicklungszeit, die Programmgröße und möglicherweise sogar die Speichernutzung beeinträchtigen kann. Wenn Sie Pokerhände sortieren, möchten Sie wirklich eine Quicksortierung, Smoothsortierung oder Mergesortierung schreiben? Ich würde mit einfacher Einfügesortierung beginnen oder ein Sortiernetz verwenden.
R ..

8
Das ist lustig. In einem Thread zur Mikrooptimierung optimieren viele Kommentatoren die Antworten. ;)
Sichern Sie sich

5
"Ich würde wetten, dass derselbe Code bald für eine Liste mit mehreren Millionen Zeilen wiederverwendet wird": Das hängt vollständig von der Problemdomäne ab. Beispiele: Wenn Sie einen Schachalgorithmus schreiben, können Sie sicher sein, dass sich die Brettgröße nicht ändert. Wenn Sie ein autonomes Fahrzeug programmieren, wächst auch die Anzahl der Räder nicht so schnell.
Nikie

3
Ich mag es nicht, "den richtigen Algorithmus zu wählen, ist niemals eine Mikrooptimierung", weil es offensichtlich wahr ist, angesichts der Natur des Wortes "richtig". Ich glaube jedoch, dass Ihre Implikation wirklich "der schnellste oder effizienteste" Algorithmus ist, mit dem ich nicht einverstanden bin. Die Wahl des effizientesten Algorithmus ist die falsche Wahl, wenn die Implementierung viel Zeit in Anspruch nimmt und die Geschwindigkeit oder der Platz dieses Segments kaum eine Rolle spielt.
Casey Patton

18

Vor ziemlich langer Zeit habe ich in meinem ersten Job Code für eingebettete Systeme geschrieben. Diese Systeme verwendeten 8086-Mikroprozessoren und wiesen einen begrenzten Speicher auf. Wir haben den Intel C Compiler verwendet. Ein System, das ich gebaut habe, musste auf ein 3-D-Array von Strukturen zugreifen. Ich habe es so aufgebaut, wie es mir in dem Buch erklärt wurde: Rufe malloc für die 3 Dimensionen auf, ordne dann Zeilen für die nächste Dimension zu, und rufe dann calloc für die Endknoten.

Es war ziemlich kompliziert (für mich zu dieser Zeit), ich musste Kurvenanpassung, ANOVA-Prozesskontrolle und Chi-Quadrat-Analyse durchführen. Es gab keine Bibliotheken, die das für uns getan hätten. wir mussten alles schreiben und alles auf den 8086 passen.

Das System lief wie ein Hund. Nach einer kurzen Profilerstellung stellte ich fest, dass der Allokator eines der größten Probleme war. Um das Problem zu beheben, habe ich alle Aufrufe von malloc aufgegeben und meine eigene Speicherverwaltung für einen großen Speicherblock durchgeführt.


In einem anderen Fall im selben Auftrag beschwerte sich der Kunde über die Reaktionszeit seines statistischen Prozessleitsystems. Das Team vor mir hatte ein "Software-SPS" -System entwickelt, bei dem die Bediener eine Boolesche Logik zum Kombinieren von Signalen und Auslöseschaltern verwenden konnten. Sie haben es in einer vereinfachten Sprache geschrieben, was wir heute als "domänenspezifische Sprache" bezeichnen würden. wie ich mich erinnere sah es so aus ((A1 + B1) > 4) AND (C1 > C2)und so weiter.

Das ursprüngliche Design analysierte und interpretierte diese Zeichenfolge jedes Mal, wenn sie ausgewertet wurde. Auf unserem dürftigen Prozessor hat dies viel Zeit in Anspruch genommen, und die Prozesssteuerung konnte nicht so schnell aktualisiert werden, wie der Prozess ausgeführt wurde.

Ich warf einen neuen Blick darauf und beschloss, diese Logik zur Laufzeit in Assembler-Code zu übersetzen. Ich habe es einmal analysiert und jedes Mal, wenn es ausgeführt wurde, hat die App eine dynamisch generierte Funktion aufgerufen. Ich glaube, so wie es manche Viren heute tun (aber ich weiß es nicht wirklich). Das Ergebnis war eine 100-fache Leistungssteigerung, die den Kunden und meinen Chef wirklich sehr glücklich machte.

Der neue Code war bei weitem nicht so wartbar, da ich einen benutzerdefinierten Compiler erstellt hatte . Der Leistungsvorteil überwog jedoch deutlich den Wartungsnachteil.


In jüngerer Zeit arbeitete ich an einem System, das eine XML-Fliege dynamisch analysieren musste. Größere Dateien würden erheblich mehr Zeit in Anspruch nehmen. Dies war sehr leistungsempfindlich; Eine zu langsame Analyse würde dazu führen, dass die Benutzeroberfläche vollständig unbrauchbar wird.

Solche Dinge tauchen die ganze Zeit auf.


Also ... manchmal möchten Sie wartbaren, einfach zu schreibenden Code. Manchmal möchten Sie Code, der schnell ausgeführt wird. Der Kompromiss ist die technische Entscheidung, die Sie für jedes Projekt treffen müssen.


9
In all Ihren Beispielen waren die Kosten für die spätere Optimierung nicht viel höher als das Schreiben des schnellen Codes von Anfang an. Daher funktionierte es in allen Fällen gut, zuerst langsameren, einfacheren Code zu schreiben und dann bei Bedarf zu optimieren.
CodesInChaos

6
@CodeInChaos: Die Antwort erhebt keinen Anspruch auf etwas anderes. Es geht um die Frage des OP: "Warum sollte ich mich um die Leistung und Effizienz von Mikros kümmern?" Probleme vor der Optimierung wurden lediglich von den anderen Antwortenden abgeleitet.
Webbiedave

12

Wenn Sie große Bilder verarbeiten und über jedes Pixel iterieren, kann die Leistungsoptimierung von entscheidender Bedeutung sein.


2
+1 - auch Hochfrequenzfinanzierung, jede Art von Audio / Video-Encoder / Decoder, Simulationen und Modellierung (z. B. Spiele), systemweite Bits wie CPU-Scheduler und Speichermanager usw.
Billy ONeal

3
KANN kritisch sein, IST aber nur dann kritisch, wenn Sie dies bewiesen und das Problem erkannt haben. (Hinweis: Es ist wahrscheinlich nicht da.)
NUR MEINE richtige MEINUNG

2
@NUR MEINE richtige MEINUNG: Für die Bildverarbeitung ist die Datenverarbeitung normalerweise der zweitgrößte Zeitkonsument (I / O ist immer noch der größte). Die Optimierung für E / A erfordert jedoch eine Menge ungewöhnlicher / verrückter Designs und deren Akzeptanz durch andere Programmierer, und manchmal ist es absolut unmöglich, Verbesserungen vorzunehmen. Der Verarbeitungsteil ist jedoch normalerweise peinlich parallelisierbar, weshalb sie leicht zu ernten sind. (Eine Optimierung kann von einer anderen als
reine

12

Lassen Sie mich etwas über das Warum hinter der Kultur erzählen .

Wenn Sie näher an 40 als an 20 Jahren sind und Ihren Lebensunterhalt mit dem Programmieren verbracht haben, dann sind Sie erwachsen geworden, als C ++ wirklich das einzige Spiel in der Stadt war, Desktop-Apps die Norm waren und die Hardware immer noch stark verzögerte Software in Bezug auf Bandbreite / Leistung.

  • Früher mussten wir dumme Programmiertricks machen, um große (> 2G) Dateien lesen zu können ...
  • Früher haben wir uns um die Größe der ausführbaren Dateien gekümmert ...
  • Früher haben wir uns Gedanken darüber gemacht, wie viel Speicher unsere Programme verbrauchen ...
  • Wir haben regelmäßig algorithmische Kompromissentscheidungen zwischen Zeit und Raum getroffen ...
  • Auch auf dem Back-End, wir hatten CGI - Programme in C oder C ++ für etwas zu behandeln einen anständigen nicht zu schreiben. von RPS ... Es war mehrere Größenordnungen schneller.
  • Früher haben wir Tests durchgeführt, um die Leistung zwischen delphi / c ++ / vb zu überprüfen!

Nur sehr wenige Menschen müssen sich heute um diese Dinge kümmern.

Vor 10 Jahren mussten Sie sich jedoch noch Sorgen machen, dass Ihre Software über ein 56-KB-Modem heruntergeladen und auf einem 5 Jahre alten PC ausgeführt werden könnte ... Erinnern Sie sich noch, wie beschissen PCs 1996 waren? Denken Sie an 4 GB Festplatte, einen 200-MHz-Prozessor und 128 MB RAM ...

Und die Server von vor 10 Jahren? Dells Server der "nächsten Generation" kostete 2000 US-Dollar und war mit 2 (!) 1-GHz-Pentium-Prozessoren, 2 Gbit oder Ram und einer 20-Gbit-Festplatte ausgestattet.

Es war einfach ein anderes Ballspiel , und all diese "erfahrenen" Ingenieure, die über 10 Jahre Erfahrung verfügen (die Jungs, die wahrscheinlich Ihre Fragen beantworten), haben sich in diesem Umfeld die Zähne geschnitten .


1
Die zusätzliche Erfahrung von 20 Jahren bedeutet auch, dass wir den Optimierungsprozess viele Male durchlaufen haben und vermeiden, Dinge zu tun, die später nötig sein könnten. Aus dem gleichen Grund habe ich mit einem Hammer nicht viel mit dem Daumen geschlagen.
Blrfl

1
loop unwinding <shudder>
red-dirt

5
und heute finden alle Kinder, die Bandbreite, CPU und Speicher für unbegrenzt hielten, dass ihre mobilen Anwendungen nicht sehr gut funktionieren.
gbjbaanb

9

Es gibt hier bereits 10 Antworten und einige sind wirklich gut, aber weil dies ein persönlicher Pet Peeve von mir ist ...

Vorzeitige Optimierung, die a) viel mehr Zeit in Anspruch nimmt als eine einfache Lösung, b) mehr Code einführt, wenn die einfache Lösung halb so groß und halb so komplex gewesen wäre, und c) Dinge unleserlicher macht, sollte ABSOLUT vermieden werden. Wenn ein Entwickler jedoch die Wahl zwischen der Verwendung von std :: map oder std :: vector hat und aus reiner Unwissenheit die falsche Sammlung für die Leistung auswählt, ist diese ebenso schlecht, wenn nicht schlechter als eine vorzeitige Optimierung. Was wäre, wenn Sie Ihren Code heute geringfügig ändern, die Lesbarkeit erhalten, die Komplexität beibehalten und ihn effizienter gestalten könnten? Oder würden Sie es "vorzeitige Optimierung" nennen? Ich finde, dass viele Leute nicht einmal darüber nachdenken würden.

Als ich der Typ war, der "Mikrooptimierung" angeraten hat, für die nur sehr geringe Änderungen erforderlich waren, erhielt ich die gleiche Antwort, die Sie gerade gesagt haben: "Sie sollten nicht zu früh optimieren. Lassen Sie es uns einfach zum Laufen bringen, und wir werden es ändern später, wenn es ein Leistungsproblem gibt ". Es hat mehrere Releases gedauert, bis wir das Problem behoben haben. Und ja, es war ein Leistungsproblem.

Eine frühe Optimierung ist vielleicht nicht gut, aber ich halte es für sehr vorteilhaft, wenn die Leute Code mit dem Verständnis schreiben, was dieser Code tun wird, und keine Frage einfach außer Acht lassen, die dazu führt, dass die O (x) -Notation "Optimierung" ist. Sie können jetzt viel Code schreiben und mit ein wenig Nachdenken über die Leistung 80% aller Probleme vermeiden.

Bedenken Sie auch, dass in Ihrer Umgebung viele Leistungsprobleme nicht sofort auftreten werden. Manchmal haben Sie einen Kunden, der das Limit überschreitet, oder ein anderer Entwickler entscheidet sich, auf Ihrem Framework aufzubauen und die Anzahl der Objekte um das Zehnfache zu erhöhen. Mit einigem Nachdenken über die Leistung könnten Sie später eine sehr kostspielige Neugestaltung vermeiden. Und wenn das Problem nach der offiziellen Veröffentlichung der Software auftritt, ist die Anwendung einer einfachen Korrektur 20-mal teurer.

Zusammenfassend lässt sich festhalten, dass die Leistung stets im Auge bleibt, um gute Gewohnheiten zu entwickeln. Was genauso wichtig ist, wie sauberen, möglichst einfachen und organisierten Code zu schreiben.


+1: Dies ist einer der Beschäftigungsfaktoren für diejenigen, die mit der Entwicklung von Shrinkwrap-Software und kommerziellen Websites beauftragt sind . Prävention ist kostengünstiger als der Fluch des Kunden.
rwong

6

Ich vermute, dass vieles, was Sie sehen, ein einfacher Stichprobenfehler ist. Wenn Menschen mit einfachen Situationen zu tun haben, schreiben sie Code und das ist das Ende der Dinge. Sie stellen Fragen, wenn sie mit etwas relativ Kniffligem zu tun haben, z. B. Optimierungsbedarf, insbesondere in einer Situation, in der es nicht unbedingt offensichtlich ist, dass eine Optimierung erforderlich ist.

Allerdings ist zweifellos auch eine vorzeitige Optimierung erforderlich. C und C ++ haben einen guten Ruf für Leistung, was tendenziell Leute anzieht, die sich für Leistung interessieren - einschließlich derer, die möglicherweise so viel für den Spaß optimieren, als weil es wirklich benötigt wird.


1
+1 - Hinweis: Die meisten SO-Fragen mit dem Tag "performance" sind wahrscheinlich Teil dieses Stichprobenfehlers: P
Billy ONeal

3
Ich sehe hier verdammt viele vorzeitige Optimierungsfragen ... Ich denke, das liegt an der Tatsache, dass viele Hobby-Programmierer mit der Idee anfangen, Spiele zu schreiben, und es gibt eine riesige Sammlung von "Optimierungs" -Büchern und Quatsch Websites zum Thema Spieleentwicklung, die Anfängern schlechte Ideen einbringen. :-)
R ..

4
Wenn Sie mit kniffligen i++++i
Dingen zu

@ Carson63000: ja das könnte die Samples total verzerren. Oder sie verbringen Zeit damit, Fragen zu beantworten, warum meine operator ++nicht kompiliert wurden.
rwong

4

Einige der anderen Antworten erwähnen eingebettete Systeme , und ich möchte darauf näher eingehen.

Es gibt viele Geräte, die Low-End-Prozessoren enthalten, zum Beispiel die Kesselsteuerung in Ihrem Haus, einen einfachen Taschenrechner oder die Dutzende von Chips in einem modernen Auto.

Um Geld zu sparen, können Flash-Speicher (zum Speichern von Code) und RAM-Speicher vorhanden sein, die für diejenigen, die nur Code für PCs oder Smartphones geschrieben haben, winzig erscheinen. Um Energie zu sparen, werden sie möglicherweise mit relativ niedrigen Taktraten betrieben.

Beispielsweise reicht die STM32-Familie von Mikrocontrollern von 24 MHz, 16 KB Flash und 4 KB RAM bis zu 120 MHz, 1 MB Flash und 128 KB RAM .

Wenn Sie Code für solche Chips schreiben, sparen Sie viel Zeit, wenn Sie darauf abzielen, Ihren Code so effizient wie möglich zu gestalten. Offensichtlich bleibt eine vorzeitige Optimierung eine schlechte Idee; Mit der Übung lernen Sie jedoch, wie häufig auftretende Probleme schnell und / oder mit minimalen Ressourcen gelöst und entsprechend codiert werden können.


1
Gute Punkte für eingebettete Systeme, ein Bereich, in dem ich selbst arbeite. Trotzdem habe ich im Laufe der Jahre die Erfahrung gemacht, dass eine fehlgeleitete Optimierung immer Zeitverschwendung ist. Ohne Hilfsmittel finden wir selten die Problembereiche
Jeff

2

Dies sind im Wesentlichen einfache Sprachen. Wenn man auf einen pathologischen Leistungsfall stößt, bei dem ein Detail, das in 99% der Fälle keine Rolle spielt, den Engpass verursacht, hat man tatsächlich die Möglichkeit, das Problem direkt zu umgehen (im Gegensatz zu den meisten anderen) Sprachen); Oft ist jedoch nicht sofort ersichtlich, wie dies am effektivsten zu erreichen ist. Daher die Hälfte der seltsamen / interessanten Fragen zur Mikrooptimierung, die hier gestellt wurden.

Die andere Hälfte kommt nur von denen, die neugierig sind, wie nahe sie dem Metall kommen können. Das sind im Wesentlichen einfache Sprachen, immerhin ...


+1: Es sollte darauf hingewiesen werden, dass die "pathologische Leistung" jedem auf der Welt passieren kann, unabhängig von Sprache oder Plattform. Die Möglichkeit der erneuten Implementierung in einer niedrigeren Sprache zum Testen und zum Disassemblieren von Lesevorgängen bietet möglicherweise mehr Erkenntnisse , ist jedoch nicht immer eine praktikable Lösung. Beispiel: "Ich weiß, dass ich es in der Assembly tun kann - aber es muss in einer teilweise vertrauenswürdigen Umgebung ausgeführt werden!"
rwong

2

Leistung ist immer ein heißes Thema, wenn Sie sich mit C und C ++ beschäftigen. In Bezug darauf, wie weit man gehen sollte, kann man immer bis zum Inline-ASM verrückt werden oder Zeigerarithmetik für eine schnellere Iteration verwenden. Es kommt jedoch vor, dass man so viel Zeit mit der Optimierung verbringt, dass die Arbeit an der Entwicklung des Gesamtprogramms zum Stillstand kommt.

Bei der Behebung dieser Probleme sind die Programmiererleistung und die Codeleistung zu berücksichtigen. Welche davon im Mittelpunkt stehen, wird immer interessante Fragen aufwerfen. Am Ende ist die wichtigste Frage, wie auffällig sie für den Benutzer ist. Arbeitet der Benutzer mit Daten, die Arrays mit Hunderten oder Tausenden von Elementen erstellen? In diesem Fall könnte sich Ihr Benutzer beschweren, dass die Standardoperationen des Programms langsam sind, um die Dinge schnell zu erledigen.

Dann gibt es den Benutzer, der mit kleinen Datenmengen arbeitet. Ein paar Dateien hier und da, in denen Dinge wie Sortieren und Dateioperationen für den Benutzer nicht so auffällig sind, wenn Sie Funktionen höherer Ebenen verwenden, die die Wartung auf Kosten einer gewissen Leistung erleichtern.

Dies ist nur ein kleines Beispiel für die Probleme, auf die Sie stoßen werden. Andere Angelegenheiten umfassen die Hardware des Zielbenutzers. Sie müssen sich viel mehr Gedanken über die Leistung machen, wenn Sie mit eingebetteten Systemen arbeiten, und wenn Ihre Benutzer beispielsweise Dual-Core-Maschinen mit mehreren RAMs haben.


Hmm .. Ich verwende keine Zeigerarithmetik für eine schnellere Iteration - es ist eine Multiplikations- und Additionsanweisung pro Schleife, unabhängig davon, ob Sie eine indexbasierte oder eine zeigerbasierte Iteration verwenden. Ich benutze es jedoch, weil es normalerweise klarer ist als eine indexbasierte Iteration.
Billy ONeal

Zeigerarithmetik ist nicht schneller als w / e.

2

Warum ist es Programmierern so wichtig? Es gibt alberne Ideen, die ihre Köpfe füllen, wie das Lösen von Leistungsproblemen, bevor sie wissen, dass sie sie haben, und das Verstehen, wenn sie raten .

Es ist schwierig, weil es meiner Erfahrung nach einige Leistungsprobleme gibt, über die man sich im Voraus Gedanken machen sollte . Es braucht Erfahrung, um zu wissen, was sie sind.

Die Methode, die ich verwende, ähnelt Ihrer, ist aber nicht dieselbe:

  1. Beginnen Sie mit einem möglichst einfachen Design. Insbesondere sollte die Datenstruktur so normal und minimal wie möglich sein. In dem Maße, in dem es unvermeidbare Redundanz gibt, sollte man sich vor Benachrichtigungen scheuen, um diese konsistent zu halten. Es ist besser, vorübergehende Inkonsistenzen zu tolerieren und sie in regelmäßigen Abständen zu beheben.

  2. Wenn sich das Programm in der Entwicklungsphase befindet, führen Sie regelmäßig eine Leistungsoptimierung durch, da sich Leistungsprobleme in unauffälliger Weise eingeschlichen haben. Die von mir verwendete Methode ist das Anhalten nach dem Zufallsprinzip , da sie meiner Meinung nach die beste ist.

Hier ist ein Beispiel für das, was ich meine.


1

Um ehrlich zu sein, kommt es darauf an, was Sie anstreben und ob Sie professionell oder als Hobby programmieren.

Moderne Computer sind heutzutage wirklich leistungsstarke Maschinen. Unabhängig davon, für welche grundlegenden Vorgänge Sie sich entscheiden, ob Sie versuchen, eine Mikrooptimierung durchzuführen oder nicht, sie können ihre Arbeit bemerkenswert schnell erledigen. Wenn Sie jedoch etwas anderes tun (z. B. Supercomputing für Bereiche wie Physik oder Chemie), möchten Sie möglicherweise so viel optimieren, wie Sie möchten.

Die frühen MIT-Programmierer waren nicht dafür geboren, großartige Sachen zu machen. Sie begannen, bestehende Algorithmen zu vereinfachen und zu optimieren. Ihr Stolz war es, 2 + 2 in zwei Sekunden weniger zu geben als der existierende Algorithmus (das ist nur ein Beispiel, Sie haben die Idee). Sie versuchten ständig, weniger Lochkarten in ihren TI-83-Maschinen für die Leistung zu verwenden.

Wenn Sie für eingebettete Systeme programmieren, müssen Sie auf jeden Fall die Leistung der Mikros im Auge behalten. Sie möchten keine langsame Digitaluhr, die 5 Nanosekunden früher als eine andere Digitaluhr tickt.

Wenn Sie ein Hobby-Programmierer sind, schadet es nicht, die kleinsten Details zu optimieren, auch wenn Ihr Programm schnell ist. Es ist nicht erforderlich, aber sicherlich etwas, an dem Sie arbeiten und die Chance nutzen können, mehr zu erfahren. Wenn Sie professionell an einer Software arbeiten, können Sie diesen Luxus nur dann in Anspruch nehmen, wenn er extrem gebraucht wird.


1
Ich glaube nicht, dass es etwas damit zu tun hat, ein Hobby-Programmierer zu sein. Nur weil Sie etwas nicht professionell machen, müssen Sie nicht unbedingt die ganze Zeit der Welt dafür aufwenden können. Darüber hinaus werden die meisten Hobbyisten größere Fehler machen, z. B. die Auswahl falscher Algorithmen, als die meisten echten Profis. Darüber hinaus arbeitet der Profi wahrscheinlich an einem Produkt, das wesentlich mehr Daten verarbeitet als der Bastler (der daher schneller sein muss) und die Kunden mit der Leistung der App zufrieden stellen muss. Hobbyisten haben keine solchen Einschränkungen.
Billy ONeal

Sie tun es nicht, aber sie haben auf jeden Fall mehr Zeit, um daran zu arbeiten, nur wenn sie wollen.

3
Ich würde das Gegenteil behaupten. Ich habe 8 Stunden oder mehr am Tag Zeit, um als Profi an etwas zu arbeiten. Ich bekomme 1, vielleicht 2 Stunden pro Tag für meine Hobbyprojekte.
Billy ONeal

1

Verwenden eines O (N2) vs O (NlogN) -Algorithmus für eine 100-Artikel-Liste.

Ich war in letzter Zeit in einer ähnlichen Situation. Ich hatte eine Reihe von Gegenständen. Im erwarteten Fall waren zwei (!) Elemente in der Liste, und selbst im schlimmsten Fall erwarte ich nicht mehr als vier oder vielleicht acht.

Ich musste diese Liste sortieren. Es stellte sich heraus, dass durch das Ersetzen std::sortdurch ein Sortiernetzwerk (im Wesentlichen viele verschachtelte ifs) ein großer Prozentsatz der Laufzeit eingespart wurde (ich erinnere mich nicht an die Zahl, aber es war ungefähr 10–20%). Dies ist ein großer Vorteil einer Mikrooptimierung, und der Code ist absolut leistungskritisch.

Natürlich habe ich das erst nach der Profilerstellung gemacht. Aber der Punkt ist, wenn ich eine Sprache verwende, die so unpraktisch und verworren ist wie C ++ (ganz zu schweigen von den ärgerlich komplexen Regeln für die Überlastungslösung), dann möchte ich alle Vorteile nutzen.


1

Kumulativer Energieverbrauch

Es gibt eine Antwort, von der ich immer denke, dass sie in dieser Diskussion fehlt und die mich ein bisschen stört - den kumulierten Energieverbrauch .

Sicher, vielleicht spielt es keine Rolle, ob Sie Ihr Programm in einer hochinterpretierten Sprache schreiben und es in einem Browser mit mehreren Indirektionsebenen ausführen lassen oder ob Ihre Schleife 0,01 Sekunden statt 0,001 Sekunden dauert. Niemand wird es bemerken, das heißt, kein einzelner Benutzer wird es bemerken.

Aber wenn Zehntausende oder sogar Millionen von Benutzern in einigen Fällen Ihren Code verwenden, summiert sich diese zusätzliche Ineffizienz. Wenn Ihr Tool verhindert, dass eine CPU für nur zehn Sekunden pro Tag in den Ruhezustand wechselt und eine Million Benutzer sie verwenden, hat Ihr ineffizienter Algorithmus nur 140 kWh [1] pro Tag zusätzlich verbraucht .

Ich sehe das selten diskutiert, und ich finde das traurig. Ich bin der festen Überzeugung, dass die Zahlen für beliebte Frameworks wie Firefox und ausgefallene interaktive Webanwendungen weitaus schlechter sind, und es wäre interessant zu recherchieren.


[1] Das habe ich mir gerade ausgedacht, 10 Millionen Sekunden mal 50 Watt. Die genaue Zahl hängt von vielen Dingen ab.


1
Sie sollten gleich mit dem Zauberwort "Handy" beginnen. Wenn eine Anwendung auf einer Desktop-Plattform ausgeführt wird, die 1 / 100sec CPU-Zeit benötigt, um einen Frame 60-mal pro Sekunde zu zeichnen, ist sie "schnell genug". Eine Verzehnfachung der Leistung würde für den Benutzer keinen Unterschied bedeuten. Auf einer mobilen Plattform kann eine Anwendung, die mit einer CPU-Auslastung von 90% ausgeführt wird, die Akkus erheblich schneller belasten als mit einer CPU-Auslastung von 10%.
Supercat

1

Manchmal gibt es nur Algorithmen, die nicht besser als eine lineare Zeit sein können, für die immer noch ein hoher Leistungsbedarf besteht.

Ein Beispiel ist die Videoverarbeitung, bei der Sie ein Bild / einen Frame nicht als grundlegendes Beispiel aufhellen können, ohne alle Pixel durchzugehen (ich nehme an, Sie können eine hierarchische Struktur verwenden, die Eigenschaften angibt, die von Kindern geerbt wurden und letztendlich in Bildkacheln übergehen) für Blattknoten, aber dann würden Sie höhere Kosten für das Durchlaufen jedes Pixels zum Renderer aufschieben, und der Code wäre wahrscheinlich schwieriger zu warten als selbst der mikrooptimierteste Bildfilter.

In meinem Bereich gibt es so viele Fälle. Ich mache tendenziell mehr Schleifen mit linearer Komplexität, die alles anfassen oder alles lesen müssen, als Schleifen, die von einer ausgefeilten Datenstruktur oder einem ausgefeilten Algorithmus profitieren. Es gibt keine Arbeit, die übersprungen werden kann, wenn alles berührt werden muss. Wenn Sie sich an diesem Punkt zwangsläufig mit linearer Komplexität befassen, müssen Sie die pro Iteration durchgeführte Arbeit immer billiger machen.

In meinem Fall sind die wichtigsten und häufigsten Optimierungen häufig Datendarstellungen und Speicherlayouts, Multithreading und SIMD (in der Regel in dieser Reihenfolge, wobei die Datendarstellung die wichtigste ist, da sie die Fähigkeit zur Ausführung der beiden letzteren beeinträchtigt). Ich habe nicht so viele Probleme, die durch Bäume, Hash-Tabellen, Sortieralgorithmen und ähnliches gelöst werden. Mein täglicher Code ist mehr im Sinne von "für jede Sache etwas tun".

Natürlich ist es ein anderer Fall, über Optimierungen zu sprechen, wenn sie notwendig sind (und noch wichtiger, wenn sie nicht notwendig sind), mikro oder algorithmisch. In meinem speziellen Fall werden jedoch, wenn ein kritischer Ausführungspfad optimiert werden muss, die 10-fachen Geschwindigkeitsgewinne häufig durch Optimierungen auf Mikroebene wie Multithreading, SIMD und Neuanordnen von Speicherlayouts und Zugriffsmustern zur Verbesserung der Referenzlokalität erzielt. Es kommt nicht so oft vor, dass ich beispielsweise eine Blasensorte durch eine Introsort- oder eine Radix-Sortierung oder eine Kollisionserkennung mit quadratischer Komplexität durch eine BVH ersetze und Hotspots finde, die zum Beispiel von der Aufteilung des heißen / kalten Feldes profitieren.

In meinem Fall ist mein Fachgebiet so leistungskritisch (Raytracing, Physik-Engines usw.), dass ein langsamer, aber perfekt korrekter Raytracer, der 10 Stunden zum Rendern eines Bildes benötigt, oft als nutzlos oder als schneller angesehen wird, der jedoch vollständig interaktiv ist gibt die hässlichsten Bilder mit Strahlen aus, die aufgrund der fehlenden wasserdichten Schnittmenge zwischen Strahlen und Dreifachstrahlen überall austreten. Geschwindigkeit ist wohl die primäre Qualitätsmetrik einer solchen Software, vielleicht sogar mehr als die Korrektheit bis zu einem gewissen Punkt (da "Korrektheit" eine unscharfe Idee beim Raytracing ist, da sich alles annähert, solange es nicht abstürzt oder so etwas). Und wenn dies der Fall ist, muss ich den Code auf der teuersten Designebene ändern, um effizientere Designs zu erstellen, wenn ich nicht im Voraus über Effizienz nachdenke. Also, wenn ich nicht '

Gaming ist ein weiteres ähnliches Feld. Es spielt keine Rolle, wie korrekt Ihre Spielelogik ist oder wie wartungsfreundlich und brillant Ihre Codebasis ist, wenn Ihr Spiel wie eine Diashow mit 1 Frame pro Sekunde ausgeführt wird. In bestimmten Bereichen könnte die mangelnde Geschwindigkeit die Anwendung für die Benutzer unbrauchbar machen. Im Gegensatz zu Spielen gibt es in Bereichen wie Raytracing keine ausreichend gute Metrik. Die Benutzer wollen immer mehr Geschwindigkeit, und der industrielle Wettbewerb ist vor allem auf der Suche nach schnelleren Lösungen. Es wird niemals gut genug sein, bis es in Echtzeit ist. Zu diesem Zeitpunkt werden Spiele Path Tracer verwenden. Und dann wird es für VFX wahrscheinlich immer noch nicht gut genug sein, denn dann möchten die Künstler vielleicht Milliarden von Polygonen laden und Partikelsimulationen mit Selbstkollision zwischen Milliarden von Partikeln mit mehr als 30 FPS durchführen.

Nun, wenn es irgendeinen Trost gibt, schreibe ich trotzdem ungefähr 90% des Codes in einer Skriptsprache (Lua), ohne Bedenken hinsichtlich der Leistung. Aber ich habe eine ungewöhnlich große Menge an Code, die tatsächlich Millionen bis Milliarden von Dingen durchlaufen muss, und wenn Sie Millionen bis Milliarden von Dingen durchlaufen, bemerken Sie einen epischen Unterschied zwischen naivem Single-Threaded-Code, der Ruft bei jeder Iteration einen Cache-Fehler auf, während beispielsweise vektorisierter Code parallel auf zusammenhängende Blöcke zugreift, in denen keine irrelevanten Daten in eine Cache-Zeile geladen werden.


0

Wie Sie bereits erwähnt haben, ist es wertlos, sich Gedanken über Probleme mit der Mikro-Leistung zu machen, bevor Sie einige Probleme erklären, die durch diese Probleme verursacht werden


0

Es ist wirklich unmöglich, diese Frage allgemein zu beantworten. Die meiste Software, die heute erstellt wird, sind interne Websites und LOB-Anwendungen, und für diese Art der Programmierung ist Ihre Argumentation ganz richtig. Auf der anderen Seite ist keine Optimierung "verfrüht", wenn Sie so etwas wie einen Gerätetreiber oder eine Spiele-Engine schreiben. Wahrscheinlich läuft Ihre Software auf sehr unterschiedlichen Systemen mit unterschiedlichen Hardwareeinschränkungen. In diesem Fall sollten Sie auf Leistung ausgelegt sein und sicherstellen, dass Sie keinen suboptimalen Algorithmus auswählen.


Genau das wollte ich sagen. Jedes Softwareteil hat seine Anwendungsdomäne und es ist nicht zu erwarten, dass es sich außerhalb dieser Domäne optimal verhält. In diesem Sinne ist vorzeitige Optimierung ein Beispiel für fehlgeleiteten Perfektionismus.
K.Steff

0

Ich denke, das Problem des Programmierers, der sich so sehr für Leistung interessiert, ist, dass er manchmal in seinem Leben mikro-performanten Code schreiben musste, vielleicht sehr dringend, und er lernte, lernte, lernte und am Ende kannte er a viele Dinge und Tricks.

Und jetzt ist es schwer zu vergessen, und ohne vorherige Messung, was zeigt, dass er sich keine Sorgen machen muss, ist er auf der sicheren Seite und verwendet schnellen Code.

Es ist immer schön, dein tiefes Wissen, deine Fähigkeiten und einige Tricks zu zeigen und etwas, was du gelernt hast, wiederzuverwenden. Sie fühlen sich wertvoll und die Zeit, die Sie für das Lernen aufwenden, ist es wert.

Manchmal habe ich in meinem Leben gelernt, dass das Präfixinkrement schneller ist ...

for (int i = 0; i < MAX; ++i)

... als Postfix-Inkrement:

for (int i = 0; i < MAX; i++)

Wenn MAX niedrig ist, spielt das keine Rolle, und wenn es echte Arbeit in der Schleife gibt, spielt das auch keine Rolle. Es gibt jedoch keinen Grund, die Postfix-Version zu verwenden, auch wenn der heutige Compiler den Code selbst optimiert.

Vielleicht brauchen die Leistungssuchenden ein zusätzliches Ziel neben dem Schreiben von 'Arbeitscode' wie 'Arbeitscode und lesbarer Code', um eine Richtlinie im großen Meer von Optionen zu haben.


0

Habe ich nur das Glück gehabt, mich nicht allzu sehr darum kümmern zu müssen, oder bin ich ein schlechter Programmierer?

Interessieren Sie sich für Ihre Anforderungen? Wenn Leistung keine Voraussetzung ist, sorgen Sie sich nicht darum. Wenn Sie viel Zeit dafür aufwenden, ist dies ein Nachteil für Ihren Arbeitgeber.

Bis zu einem gewissen Grad ist Leistung immer eine Voraussetzung. Wenn Sie es schlagen können, ohne darüber nachzudenken, haben Sie das Recht, nicht darüber nachzudenken.

Persönlich bin ich am häufigsten von der Leistung abhängig, wenn meine Tests zu lange dauern. Ich bin zu ungeduldig, um 5 Minuten zu warten, während eine Reihe von Tests bestanden wird. Aber das wird normalerweise gelöst, indem man mit den Tests spielt.

Meine Frage ist, warum es so viele Programmierer interessiert? Ist es wirklich ein Problem für die meisten Entwickler,

Es gibt eine große Anzahl von Programmierern, die berechtigt sind, wie sehr sie sich darum kümmern. Es gibt viele, die nicht sind. Reden wir über die, die es nicht sind.

Eines der ersten Dinge, die Programmierer in der Schule lernen, nachdem sie gelernt haben, wie die Dinge tatsächlich funktionieren, ist die große O-Notation. Viele von ihnen lernen die Lektion richtig und konzentrieren sich daher richtig auf Dinge, die dramatisch von n beeinflusst werden. Andere verstehen die Mathematik nicht und nehmen nur die Lektion weg, die schnell sein muss, wenn sie einmal funktioniert. Schlimmer noch, einige dieser Schüler lernen nie etwas anderes darüber, was mit Ihrem Code zu tun ist, außer dass er funktioniert und schnell arbeitet. Die verpassten Lektionen: lesbar machen, gut gestalten, nicht grundlos herumspielen.

Knuth hatte recht: Vorzeitige Optimierung ist die Wurzel allen Übels. Aber wenn es funktioniert, was ist der nächste Schritt? Schnell, oder? NEIN! Der nächste Schritt ist lesbar. Lesbar ist der erste, nächste, mittlere und letzte Schritt. Viele der Leute, die nicht benötigte Leistungsoptimierungen vornehmen, werfen die Lesbarkeit unter den Bus.

Einige bekommen sogar einen perversen Nervenkitzel, wie unlesbar ihr Code ist. Sie mussten sich mit schwer verständlichem Code auseinandersetzen, der von anderen erstellt wurde. Jetzt sind sie an der Reihe, sich zu amortisieren.

Ich weiß das, weil ich das gemacht habe. Ich habe einmal eine perfekt lesbare 5-Zeilen-Struktur zu einem nicht entzifferbaren einzeiligen booleschen Ausdruck überarbeitet und ihn stolz an meinen Professor geschickt. Ich habe nicht das Lob bekommen, auf das ich gehofft hatte.

Wenn Code lesbar bleibt, ist es einfach, ihn später schnell zu machen. Deshalb betont Knuth "vorzeitig" und nicht "unnötig". Denn schneller ist sicher besser. Aber besser ist nur besser, je nachdem, was Sie dafür opfern. Warten Sie also, bis Sie wissen, welche Leistung Sie wirklich benötigen, bevor Sie dafür Opfer bringen. Opfere widerwillig die Lesbarkeit, denn wenn sie einmal weg ist, ist es schwierig, wieder dorthin zu gelangen.

Jenseits der Lesbarkeit liegt die ganze Welt des Software-Designs. Worum geht es auf dieser Seite? Einige haben keine Ahnung, was Design angeht. Da sie also nicht mit Design beeindrucken können, machen sie ein unkenntliches Durcheinander, sodass die Leute nicht erkennen können, dass sie keine Ahnung haben. Da niemand jemals seinen Code repariert, muss es ein guter Code sein, oder?

Für manche ist Leistung der Haken, um zu tun, was sie wollen. Programmierer haben viel Kraft und Autonomie. Ihnen wurde Vertrauen entgegengebracht. Missbrauche das Vertrauen nicht.

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.