Was macht ein großes und komplexes Softwareprodukt langsam? [geschlossen]

16

Aus einem weitgehend irrelevanten Grund habe ich Delphi 7 in so langer Zeit erneut installiert. Ich muss sagen, ich war total überwältigt - in gewisser Weise schon länger nicht mehr. So kann ich mich überhaupt nicht erinnern. Die Installation dauerte ca. 30 Sekunden. Der Start dauerte 2 Sekunden und war sofort einsatzbereit. Ich kann "Ausführen" in der Sekunde nach dem Start drücken, und weniger als eine Sekunde später ist das leere Programm bereits sichtbar und läuft. Hurra für Computer, die so viel schneller werden!

Aber der Grund, warum ich so hin und weg bin, ist, dass ich normalerweise Visual Studio 2010 verwende, das sich überhaupt nicht so bissig anfühlt. Zugegeben, 7 Delphi ist ein viel kleineres System als Visual Studio 2010, aber es hat die haben Erscheinung alle die dort wirklich notwendigen Dinge aufweist: eine Steuerpalette, ein Formular - Designer, einen Code - Editor mit Code - Vervollständigung. Mir ist klar, dass die Sprache möglicherweise einfacher ist und die Codevervollständigung möglicherweise weniger leistungsfähig ist und die IDE möglicherweise nicht annähernd so erweiterbar und funktionsreich ist, aber dennoch: Ich verstehe nicht, wie (dh durch welchen Mechanismus) dies funktioniert Viele zusätzliche Funktionen (die ich möglicherweise noch nicht einmal ausgelöst habe) führen dazu, dass ein System wie Visual Studio im Vergleich immer träge wirkt.

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?

Roman Starkov
quelle
7
Einfach: Mit zunehmender Masse wird mehr Kraft benötigt, um die Trägheit zu überwinden.
Shog9
Jemand hat es mir einmal von Managern erzählt, aber ich glaube das überhaupt nicht.
Michael Grassman
1
Dies ist ein großer Teil des Grundes, warum ich immer noch hauptsächlich D7 für die Delphi-Programmierung benutze.
GroßmeisterB
Der schnellste Code ist der, der niemals ausgeführt wird.
Henry
4
@romkyns: Ich finde, dass Software in der heutigen Zeit oft unglaublich aufgebläht, unnötig groß und unhandlich ist. Ein Großteil der Software löst jetzt dieselben Probleme, die vor zehn oder sogar zwanzig Jahren gelöst wurden, mit einem Bruchteil der Leistung und des Platzes. Warum liegt es immer noch so weit zurück, wenn nicht noch weiter? Ineffizienz und Aufblähen.
Orbling

Antworten:

20

Architektur-Astronautik

Visual Studio 2010 basiert auf Windows Presentation Foundation. Schauen Sie sich die Button-Klasse für WPF an. Es ist das 9. Kind einer Basisklasse. Es hat ungefähr 5 Seiten mit Eigenschaften, Methoden und Ereignissen. Hinter den Kulissen befinden sich weitere fünf Seiten mit Stildefinitionen, die die wunderschön abgerundeten Ecken und die subtilen Animationsübergänge beschreiben, wenn sich ein Mauszeiger darüber bewegt. Dies ist alles für etwas, das im Grunde genommen Text oder ein Bild anzeigt und ein Klickereignis erzeugt, wenn eine Maustaste herunterfällt.

Beenden Sie ein Programm wie Visual Studio an einer beliebigen Stelle. Schauen Sie sich den Stack-Trace an. Die Chancen stehen sehr gut, dass Sie 20 Ebenen tief im aufrufenden Stapel sind und dass fünf DLLs geladen wurden, um dorthin zu gelangen.

Vergleichen Sie nun diese beiden Dinge mit Delphi. Ich wette, Sie stellen fest, dass ein Delphi-Button nur 20 Eigenschaften, Methoden und Ereignisse enthält. Ich wette, die Delphi-IDE hat nur einen Stack-Trace mit einer Tiefe von 5-7 Ebenen. Denn wenn Computer langsamer waren, konnte man den Overhead von Visual Studio 2010 einfach nicht ertragen, ohne dass die IDE 40 Minuten brauchte, um zu starten :-)

Ist einer besser als der andere? Nun, ich kann einem Delphi-Programm im Allgemeinen sagen, wann es geladen wird, weil es flach aussieht, die Farben stummgeschaltet sind (vielleicht 8 Bit?) Und es keine subtilen Schattierungen oder Animationen gibt. Ich fühle mich heutzutage einfach "billig". Günstig, aber schnell.

Geht es uns besser? Das ist eine Frage für die Philosophen, nicht für die Programmierer.

Jay Beavers
quelle
4
Ein Delphi-Programm sieht nicht flach aus. Vielmehr programmiert ein Programmierer ein Programm so , dass es flach aussieht. Sie können mit Delphi genau wie in C # oder C ++ gut aussehende, moderne, vollfarbige Benutzeroberflächen erstellen.
GroßmeisterB
2
Dies ist eine aufschlussreiche Antwort. aber ich bin nicht sicher, ob es vollständig ist. Visual Studio 2008 (der Vorgänger von 2010) enthält kein WPF und ist immer noch um Welten langsamer als Delphi 7. Würden Sie immer noch dasselbe über die Tiefe des Aufrufstapels und die Anzahl der geladenen DLLs sagen?
Timwi
3
@ Timwi Ja, absolut würde ich. Ich ging weniger auf das Böse von WPF ein (eigentlich mag ich WPF) als vielmehr darauf, wie wir dazu neigen, Schichten über Schichten der Softwareabstraktion hinzuzufügen, wenn wir die Wahl haben. Vielleicht hatte Visual Studio 2008 nicht so viel Overhead, aber wie Sie bemerkt haben, hatte es genug :-)
Jay Beavers
@GrandmasterB, ich knalle Delphi nicht, weil es mit weniger Annahmen und einfacheren Bibliotheken kommt. WPF wurde unter der Annahme entwickelt, dass die GPU-Hardwarebeschleunigung es Programmen ermöglicht, tiefere Farben, häufige Animationen, Alpha-Überblendungen, Schatten usw. zu verwenden. Delphi wurde zu einer Zeit entwickelt, als diese Annahmen nicht getroffen werden konnten. Könnten Sie dies alles in Delphi erneut implementieren? Sicher, aber Sie müssten viel Code eingeben, um das Verhalten einer WPF-Schaltfläche zu erhalten. Auf der positiven Seite ist zu erwähnen, dass eine Delphi-Schaltfläche keine CPU-, Speicher- und GPU-Anforderungen hat. Dies war auch die Frage des @ OP.
Jay Beavers
10
Ihr Argument für eine flache und einfache Benutzeroberfläche wird durch die neue "moderne" Benutzeroberfläche von Windows 10 vollständig ungültig. Jetzt haben wir den ganzen Aufwand, um flache, quadratische, schlichte Knöpfe zu erstellen, wie wir es vor 30 Jahren getan haben.
Gbjbaanb
11

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 Pixeloder Boolean.

Aber ich sehe eine Menge Quellcode für Programme , die eine schwere Last bewältigen tun (zB: Umgang mit Hunderttausenden bis Millionen Pixeloder BooleanInstanzen) , 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.

Thomas Owens
quelle
2
Technische Schulden also. Technische Schulden, die sich nie auszahlen.
Robert Harvey
1
Robert ist richtig. Ein Fehler von einem Typ, zweihundert Fehler --forcevon Managern, die schreien "Sie werden gefeuert, wenn Sie dies nicht bis morgen implementieren", die Jahre guter Softwareentwicklungspraktiken, TDD, Komponententests und aller menschlichen und vernünftigen Programmierprinzipien zunichte machen und zwei weitere Male, in denen Sie müde waren ... dieser Typ, der die Firma verrückt gemacht hat, weil er ohne Grund entlassen wurde und die Codebasis durcheinander gebracht hat ... diese nicht mehr erhältlichen Bibliotheken, die Sie nie aktualisiert haben ... und hier haben Sie sie: die köstliche Spaghetti-Codebasis und aufgeblähte Software. Guten Appetit
user3834459
2
Interessant, vor allem, wie Sie einen Missbrauch exzessiver Granularität gesehen haben. Ich habe mich dabei erwischt, wie ich in der Vergangenheit gelegentlich etwas Ähnliches gemacht habe, was zu einer schlechten Leistung führte. Dies ist ganz ähnlich zu Ihrer Antwort von vor ein paar Tagen, in der es darum geht, Sammlungen und Massenalgorithmen der übermäßigen Granularität vorzuziehen . Ich kann nicht glauben, dass diese Antwort für ihre Tiefe nicht mehr geschätzt wurde. Es bringt mich dazu, einige der Designs, die ich im Laufe der Jahre gebaut habe, zu überdenken. Ich frage mich, warum diese Techniken nicht weiter verbreitet sind.
Mike unterstützt Monica am
2
@Mike Ich bin ein bisschen kaputt, wenn es darum geht, eine datenorientierte Denkweise zu fördern. Es ist beliebt in der Spieleindustrie, wo versucht wird, jeden Zentimeter der Hardware zu nutzen. Das heißt, es reduziert zugegebenermaßen die Flexibilität. Wenn Sie eine abstrakte Pixelklasse haben, können Sie verrückte Dinge damit machen, wie ein einzelnes Bild, das zwei oder mehr verschiedene Pixelformate mischt! Wenn es sich jedoch um kritische Pfade handelt, würde wahrscheinlich kein Bild von dieser Flexibilität profitieren, und die Leistung wird bei allem, was Bilder und Pixel betrifft, zu einem echten Problem.
1
In den schlechten alten Zeiten habe ich Code implementiert, um die Grafik-APIs zu umgehen und für einen wichtigen Teil meines Codes direkt auf Pixel im Speicher zuzugreifen. Der Unterschied zwischen den vielen Abstraktions- und Direktzugriffsebenen war etwa das 100-fache, was in jenen Tagen für einen Computer von Bedeutung war. Jetzt sind Ihre Computer schnell genug, um jede Art von Abstraktion zu meistern, wenn Sie müssen.
Michael Shopsin