Jeder sagt, ich sollte meinen Code modular gestalten, aber ist es nicht weniger effizient, wenn ich mehr Methodenaufrufe als weniger, aber größere Methoden verwende? Was ist der Unterschied in Java, C oder C ++?
Ich habe festgestellt, dass das Bearbeiten, Lesen und Verstehen einfacher ist, insbesondere in einer Gruppe. Ist der Rechenzeitverlust im Vergleich zu den Vorteilen für die Code-Ordnung unbedeutend?
java
c++
c
efficiency
Fatsokol
quelle
quelle
Antworten:
Ja, das ist irrelevant.
Computer sind unermüdliche, nahezu perfekte Ausführungsmotoren, die mit einer Geschwindigkeit arbeiten, die mit dem Gehirn nicht zu vergleichen ist. Während ein Funktionsaufruf die Ausführungszeit eines Programms messbar verlängert, entspricht dies nicht der zusätzlichen Zeit, die das Gehirn der nächsten am Code beteiligten Person benötigt, um die unlesbare Routine zu entwirren sogar zu beginnen zu verstehen , wie mit ihm zu arbeiten. Sie können die Berechnung als Scherz versuchen - nehmen Sie an, dass Ihr Code nur einmal gepflegt werden muss und dass dies nur eine halbe Stunde länger dauert, bis sich jemand mit dem Code vertraut gemacht hat. Nehmen Sie Ihre Prozessortaktfrequenz und berechnen: wie oft würde der Code laufen müssen , um noch zu träumen , dass der Ausgleich?
Kurz gesagt, Mitleid mit der CPU ist in 99,99% der Fälle völlig falsch. Verwenden Sie in den seltenen verbleibenden Fällen Profiler. Gehen Sie nicht davon aus, dass Sie diese Fälle erkennen können - das können Sie nicht.
quelle
Es hängt davon ab, ob.
In der gletscherschwachen Welt der Web-Programmierung, in der alles mit menschlicher Geschwindigkeit abläuft, spielt methodenintensives Programmieren, bei dem die Kosten des Methodenaufrufs mit den Kosten der von der Methode durchgeführten Verarbeitung vergleichbar sind oder diese übersteigen, wahrscheinlich keine Rolle .
In der Welt der eingebetteten Systemprogrammierung und Interrupt-Handler für Interrupts mit hohen Raten spielt dies mit Sicherheit eine Rolle. In dieser Umgebung sind die üblichen Modelle "Speicherzugriff ist billig" und "Prozessor ist unendlich schnell" ausgefallen. Ich habe gesehen, was passiert, wenn ein objektorientierter Mainframe-Programmierer seinen ersten Interrupt-Handler mit hoher Rate schreibt. Es war nicht hübsch.
Vor einigen Jahren habe ich nicht-rekursives 8-Wege-Konnektivitäts-Blob-Coloring mit Echtzeit-FLIR-Bildern auf einem zu dieser Zeit anständigen Prozessor durchgeführt. Der erste Versuch verwendete einen Unterprogrammaufruf, und der Unterprogrammaufruf-Overhead verschlang den Prozessor am Leben. (4 Aufrufe PRO PIXEL x 64K Pixel pro Bild x 30 Bilder pro Sekunde = Sie finden es heraus). Beim zweiten Versuch wurde die Unterroutine in ein C-Makro geändert, ohne dass die Lesbarkeit darunter litt, und alles bestand aus Rosen.
Sie müssen genau hinschauen, was Sie tun und in welchem Umfeld Sie es tun werden.
quelle
Zuallererst: Programme in einer höheren Sprache sollen von Menschen gelesen werden, nicht von Maschinen.
Schreiben Sie die Programme so, dass Sie sie verstehen. Denken Sie nicht an die Leistung (wenn Sie ernsthafte Leistungsprobleme haben, profilieren Sie Ihre Anwendung und verbessern Sie die Leistung dort, wo sie benötigt wird).
Selbst wenn es wahr ist, dass das Aufrufen einer Methode oder Funktion einen gewissen Aufwand erfordert, spielt dies keine Rolle. Heutzutage sollten Compiler in der Lage sein, Ihren Code in eine effiziente Maschinensprache zu kompilieren, damit der generierte Code für die Zielarchitektur effizient ist. Verwenden Sie die Optimierungsschalter Ihres Compilers, um den effizienten Code zu erhalten.
quelle
Wenn Sie ansonsten eine große Funktion hätten und diese in viele kleinere aufteilen , werden diese kleineren inline dargestellt, da in diesem Fall der einzige Nachteil des Inlinings (zu viele Wiederholungen derselben Anweisungen) nicht relevant ist. Das heißt, Ihr Code verhält sich so, als hätten Sie eine große Funktion geschrieben.
Wenn sie aus irgendeinem Grund nicht inline sind und dies zu einem Leistungsproblem wird, sollten Sie manuelles Inlining in Betracht ziehen. Nicht alle Anwendungen sind vernetzte CRUD-Formulare mit enormen Latenzen.
quelle
Es fallen wahrscheinlich keine Berechnungskosten an. In der Regel beschäftigen sich die Compiler / JITs der letzten 10-20 Jahre mit der Funktion Inlining einwandfrei. Für C / C ++ ist es normalerweise auf "inlinierbare" Funktionen beschränkt (dh die Definition der Funktion steht dem Compiler während der Kompilierung zur Verfügung - das heißt, sie befindet sich im Header derselben Datei), aber die aktuellen Techniken von LTO überwinden dies.
Ob Sie Zeit für die Optimierung aufwenden sollten, hängt von dem Bereich ab, an dem Sie arbeiten. Wenn Sie mit „normalen“ Anwendungen arbeiten, die die meiste Zeit auf Eingaben gewartet haben, sollten Sie sich wahrscheinlich keine Gedanken über Optimierungen machen, es sei denn, die Anwendung fühlt sich langsam an.
Auch in solchen Fällen sollten Sie sich vor der Mikrooptimierung auf viele Dinge konzentrieren:
O(n)
zuO(log n)
könnte viel größere Auswirkungen haben als alles, was Sie durch Mikrooptimierung erreichen könnten.List
wenn Sie es benötigen,HashSet
damit SieO(n)
nachschlagen können, wann Sie es haben könntenO(1)
.Selbst wenn Sie entscheiden, dass Sie eine Mikrooptimierung durchführen müssen (was praktisch bedeutet, dass Ihre Software in HPC verwendet wird, eingebettet ist oder nur von einer sehr großen Anzahl von Personen verwendet wird - andernfalls überwiegen die zusätzlichen Wartungskosten die Computerzeitkosten), die Sie benötigen um die Hotspots (Kernel) zu identifizieren, die Sie beschleunigen möchten. Aber dann solltest du wahrscheinlich:
Als letzte Bemerkung. Das einzige Problem, das Sie bei Methodenaufrufen haben, sind normalerweise die indirekten Sprünge (virtuelle Methoden), die vom Verzweigungsprädiktor nicht vorhergesagt wurden (leider ist der indirekte Sprung der schwierige Fall dafür). Jedoch:
quelle
Meine Antwort wird wahrscheinlich nicht zu sehr auf die vorhandenen Antworten eingehen, aber ich bin der Meinung, dass meine zwei Cent hilfreich sein könnten.
Zuerst; ja, aus Gründen der Modularität verzichten Sie normalerweise auf eine gewisse Ausführungszeit. Wenn Sie alles in Assembler-Code schreiben, erhalten Sie die beste Geschwindigkeit. Das gesagt...
Kennst du YouTube? Wahrscheinlich der Standort mit der höchsten Bandbreite oder der zweitgrößte nach Netflix? Sie schreiben einen großen Teil ihres Codes in Python, einer hochmodularen Sprache, die nicht ganz für erstklassige Leistung ausgelegt ist.
Die Sache ist, wenn etwas schief geht und sich Benutzer über das langsame Laden von Videos beschweren, gibt es nicht viele Szenarien, in denen diese Langsamkeit letztendlich auf die langsame Ausführungsgeschwindigkeit von Python zurückzuführen ist. Die schnelle Neukompilierung von Python und die modulare Möglichkeit, neue Dinge ohne Typprüfung auszuprobieren, werden es den Ingenieuren jedoch wahrscheinlich ermöglichen, das Problem schnell zu beheben ("Wow. Unser neuer Praktikant hat eine Schleife geschrieben, die eine neue SQL-Unterabfrage ausführt für JEDES Ergebnis. ") oder (" Oh, Firefox hat dieses alte Caching-Header-Format verworfen und eine Python-Bibliothek erstellt, um das neue einfach einzurichten ")
In diesem Sinne kann eine modulare Sprache auch in Bezug auf die Ausführungszeit als schneller angesehen werden, da es wahrscheinlich einfacher sein wird, den Code neu zu organisieren, damit er optimal funktioniert, sobald Sie die Engpässe ermittelt haben. So viele Ingenieure werden Ihnen sagen, dass die schweren Performance-Hits nicht dort waren, wo sie gedacht hatten (und tatsächlich wurden die Dinge, die sie optimiert haben, kaum benötigt oder funktionierten nicht einmal so, wie sie es erwartet hatten!).
quelle
Ja und nein. Wie andere Programme bereits angemerkt haben, zuerst auf Lesbarkeit, dann auf Effizienz. Es gibt jedoch Standardverfahren, die sowohl lesbar als auch effizient sind. Der meiste Code wird eher selten ausgeführt, und Sie werden von der Optimierung sowieso nicht viel profitieren.
Java kann kleinere Funktionsaufrufe einbinden, so dass es kaum einen Grund gibt, das Schreiben der Funktionen zu vermeiden. Optimierer arbeiten in der Regel besser mit einfacher zu lesendem Code. Es gibt Studien, die Verknüpfungen aufzeigen, die theoretisch schneller ablaufen sollten, tatsächlich aber länger dauern. Der JIT-Compiler funktioniert wahrscheinlich besser, der Code ist kleiner und häufig ausgeführte Teile können identifiziert und optimiert werden. Ich habe es nicht ausprobiert, aber ich würde erwarten, dass eine große Funktion, die relativ selten aufgerufen wird, nicht kompiliert wird.
Dies trifft wahrscheinlich nicht auf Java zu, aber eine Studie ergab, dass größere Funktionen tatsächlich langsamer ausgeführt werden, da ein anderes Speicherreferenzmodell erforderlich ist. Dies war Hardware- und Optimierungsspezifisch. Für kleinere Module wurden Anweisungen verwendet, die innerhalb einer Speicherseite arbeiteten. Diese waren schneller und kleiner als die Anweisungen, die erforderlich waren, wenn die Funktion nicht zu einer Seite passte.
Es gibt Fälle, in denen es sich lohnt, den Code zu optimieren, aber im Allgemeinen müssen Sie den Code profilieren, um festzustellen, wo er sich befindet. Ich finde, es ist oft nicht der Code, den ich erwartet habe.
quelle