Ich möchte Menschen, die Erfahrung mit der Arbeit mit Systemen haben, nach dem Maßstab von Visual Studio fragen: Was macht sie langsam? Sind es die Schichten auf Schichten von Abstraktionen, die erforderlich sind, um die Codebasis innerhalb der menschlichen Verständnisfähigkeiten zu halten? Ist es die Menge an Code, die durchlaufen werden muss? Ist es die moderne Tendenz zu zeitsparenden Ansätzen für Programmierer, die (umwerfend) hohe Kosten in der Abteilung für Taktzyklen / Speichernutzung verursachen?
Ich glaube, Sie haben eine Reihe davon erraten, aber ich möchte Ihnen den meiner Meinung nach größten Faktor anbieten, da ich an einer einigermaßen großen Codebasis gearbeitet habe (nicht sicher, ob sie so groß ist wie Visual Studio - in Millionen von Codezeilen) Kategorie und etwa tausend Plugins) seit etwa 10 Jahren und beobachtende Phänomene auftreten.
Es ist auch ein bisschen weniger kontrovers, da es nicht auf APIs oder Sprachfunktionen oder ähnliches eingeht. Diese beziehen sich auf "Kosten", die eher zu einer Debatte als zu "Ausgaben" führen können, und ich möchte mich auf "Ausgaben" konzentrieren.
Lose Koordination und Vermächtnis
Was ich beobachtet habe, ist, dass eine lose Koordination und ein langes Vermächtnis dazu neigen, eine Menge Müll anzusammeln.
Zum Beispiel fand ich in dieser Codebasis ungefähr einhundert Beschleunigungsstrukturen, von denen viele überflüssig sind.
Wir hätten gerne einen KD-Baum zum Beschleunigen einer Physik-Engine, einen anderen für eine neue Physik-Engine, die häufig parallel zur alten ausgeführt wird. Wir hätten Dutzende Implementierungen von Octrees für verschiedene Mesh-Algorithmen, einen weiteren KD-Baum zum Rendern , Kommissionierung usw. usw. usw. Dies sind alles große, sperrige Baumstrukturen, die zur Beschleunigung der Suche verwendet werden. Jeder einzelne kann Hunderte von Megabyte bis Gigabyte Speicher für eine Eingabe mit sehr durchschnittlicher Größe benötigen. Sie wurden nicht immer instanziiert und verwendet, aber zu einem bestimmten Zeitpunkt könnten sich 4 oder 5 von ihnen gleichzeitig im Speicher befinden.
Jetzt speicherten alle genau dieselben Daten, um die Suche nach ihnen zu beschleunigen. Sie können es sich wie die analoge alte Datenbank vorstellen, die alle ihre Felder in 20 verschiedenen redundanten Karten / Wörterbüchern / B + -Bäumen auf einmal speichert, die mit denselben Schlüsseln identisch organisiert sind und die alle ständig durchsucht. Jetzt nehmen wir 20-mal den Speicher und die Verarbeitung.
Darüber hinaus bleibt aufgrund der Redundanz nur wenig Zeit, um einen von ihnen mit dem damit verbundenen Wartungspreis zu optimieren, und selbst wenn dies der Fall wäre, hätte er nur 5% der Wirkung, die er im Idealfall erzielen würde.
Was verursacht dieses Phänomen? Lose Koordination war die häufigste Ursache, die ich sah. Viele Teammitglieder arbeiten häufig in ihren isolierten Ökosystemen und entwickeln oder verwenden Datenstrukturen von Drittanbietern, verwenden jedoch nicht dieselben Strukturen, die andere Teammitglieder verwendet haben, selbst wenn es sich um geradezu offensichtliche Duplikate genau derselben Bedenken handelte.
Was bewirkt, dass dieses Phänomen anhält? Vermächtnis und Kompatibilität waren die Hauptursache, die ich sah. Da wir die Kosten für die Implementierung dieser Datenstrukturen bereits bezahlt haben und große Mengen an Code von diesen Lösungen abhängig waren, war es oft zu riskant, sie auf weniger Datenstrukturen zu konsolidieren. Obwohl viele dieser Datenstrukturen konzeptionell hochgradig redundant waren, waren sie in ihren Schnittstellendesigns nicht immer annähernd identisch. Ihre Ersetzung wäre also eine große, riskante Änderung gewesen, anstatt sie nur Speicherplatz und Verarbeitungszeit in Anspruch nehmen zu lassen.
Speichereffizienz
In der Regel hängen die Speichernutzung und die Geschwindigkeit zumindest auf der Bulk-Ebene zusammen. Sie können langsame Software oft daran erkennen, wie sie den Speicher voll macht. Es ist nicht immer wahr, dass mehr Speicher zu einer Verlangsamung führt, da es auf "heißen" Speicher ankommt (auf welchen Speicher ständig zugegriffen wird - wenn ein Programm eine Schiffsladung Speicher verwendet, aber nur 1 Megabyte davon verwendet werden Zeit, dann ist es nicht so eine große Sache, geschwindigkeitsmäßig).
So können Sie die potenziellen Schweine basierend auf der Speichernutzung viel Zeit erkennen. Wenn eine Anwendung beim Start Dutzende bis Hunderte von Megabyte Speicher benötigt, ist dies wahrscheinlich nicht sehr effizient. Dutzende Megabyte mögen heutzutage klein erscheinen, wenn wir Gigabyte DRAM haben, aber die größten und langsamsten CPU-Caches liegen immer noch im Bereich von knappen Megabyte und die schnellsten liegen immer noch im Bereich von Kilobyte. Infolgedessen verbraucht ein Programm, das nur zum Starten 20 Megabyte benötigt und nichts tut, aus der Sicht des Hardware-CPU-Cache immer noch ziemlich viel Speicher, insbesondere wenn wiederholt und auf alle 20 Megabyte dieses Speichers zugegriffen wird häufig, während das Programm ausgeführt wird.
Lösung
Für mich besteht die Lösung darin, koordiniertere, kleinere Teams zu suchen, um Produkte zu bauen. Diese Teams können ihre "Ausgaben" nachverfolgen und vermeiden, immer und immer wieder dieselben Artikel zu "kaufen".
Kosten
Ich werde in die umstrittenere "Kostenseite" eintauchen, nur ein kleines bisschen mit einem "Ausgaben" -Phänomen, das ich beobachtet habe. Wenn eine Sprache ein unvermeidbares Preisschild für ein Objekt hat (wie eines, das Laufzeitreflexion bietet und keine zusammenhängende Zuordnung für eine Reihe von Objekten erzwingen kann), ist dieses Preisschild nur im Kontext eines sehr granularen Elements wie a teuer einzeln Pixel
oder Boolean
.
Aber ich sehe eine Menge Quellcode für Programme , die eine schwere Last bewältigen tun (zB: Umgang mit Hunderttausenden bis Millionen Pixel
oder Boolean
Instanzen) , dass die Kosten einer solchen granularen Ebene zu bezahlen.
Objektorientiertes Programmieren kann das irgendwie verschlimmern. Es sind jedoch nicht die Kosten für "Objekte" an sich oder gar OOP, sondern nur die Kosten, die auf einer so detaillierten Ebene eines jugendlichen Elements gezahlt werden, das millionenfach instanziiert wird.
Das sind also die anderen Phänomene der Kosten und Ausgaben, die ich beobachte. Die Kosten betragen ein paar Cent, aber ein paar Cent summieren sich, wenn wir eine Million Dosen Soda einzeln kaufen, anstatt mit einem Hersteller über einen Großeinkauf zu verhandeln.
Die Lösung hier für mich ist "Großeinkauf". Objekte sind auch in Sprachen, die jeweils ein paar Cent kosten, vollkommen in Ordnung, vorausgesetzt, diese Kosten werden für das analoge Äquivalent einer Getränkedose nicht millionenfach einzeln gezahlt.
Vorzeitige Optimierung
Ich mochte die Formulierung, die Knuth hier verwendete, nie so recht, weil "vorzeitige Optimierung" die realen Produktionsprogramme selten schneller macht. Einige interpretieren dies als "frühzeitig optimieren", wenn Knuth eher "ohne die richtigen Kenntnisse / Erfahrungen optimieren, um die tatsächlichen Auswirkungen auf die Software zu kennen" meinte. Wenn überhaupt, wird der praktische Effekt einer vorzeitigen Optimierung die Software häufig verlangsamen , da aufgrund der verschlechterten Wartbarkeit nur wenig Zeit bleibt , um die wirklich wichtigen kritischen Pfade zu optimieren .
Dies ist das letzte Phänomen, das ich beobachtete, als Entwickler, die beim Kauf einer einzigen Dose Soda ein paar Cent sparen wollten, nie wieder ein Haus kauften oder noch schlimmer, all ihre Zeit damit verschwendeten, ein paar Cent (oder noch schlimmer, imaginäre Cent) zu kneifen Unverständnis ihres Compilers oder der Architektur der Hardware), als Milliarden von Dollar verschwenderisch an anderer Stelle ausgegeben wurden.
Die Zeit ist sehr begrenzt. Wenn wir also versuchen, Absolutes zu optimieren, ohne über die richtigen Kontextinformationen zu verfügen, haben wir oftmals nicht die Möglichkeit, die wirklich wichtigen Stellen zu optimieren. Aus praktischen Gründen würde ich sagen, dass "vorzeitige Optimierung Software viel langsamer macht. "
Das Problem ist, dass es Entwicklertypen gibt, die das, was ich oben über Objekte geschrieben habe, nehmen und versuchen, einen Codierungsstandard zu etablieren, der die objektorientierte Programmierung oder etwas Verrücktes dieser Art verbietet. Effektive Optimierung ist eine effektive Priorisierung, und es ist absolut wertlos, wenn wir in einem Meer von Wartungsproblemen ertrinken.