Es scheint mir, dass alles, was mit einem Stapel gemacht werden kann, mit dem Haufen gemacht werden kann, aber nicht alles, was mit dem Haufen gemacht werden kann, kann mit dem Stapel gemacht werden. Ist das korrekt? Dann, der Einfachheit halber, und selbst wenn wir bei bestimmten Workloads ein wenig an Leistung verlieren, könnte es nicht besser sein, nur einen Standard (dh den Heap) zu verwenden?
Denken Sie an den Kompromiss zwischen Modularität und Leistung. Ich weiß, dass dies nicht der beste Weg ist, um dieses Szenario zu beschreiben, aber im Allgemeinen scheint es, dass die Einfachheit des Verstehens und des Designs eine bessere Option sein könnte, selbst wenn das Potenzial für eine bessere Leistung besteht.
Antworten:
Haufen sind schlecht bei schneller Speicherzuweisung und Freigabe. Wenn Sie für einen begrenzten Zeitraum viele kleine Mengen an Arbeitsspeicher abrufen möchten, ist ein Heap nicht die beste Wahl. Ein Stack mit seinem supereinfachen Allocation / Deallocation-Algorithmus ist natürlich das Beste (noch mehr, wenn er in die Hardware integriert ist), weshalb die Leute ihn zum Beispiel zum Übergeben von Argumenten an Funktionen und zum Speichern lokaler Variablen verwenden - am häufigsten Ein wichtiger Nachteil ist, dass der Platz begrenzt ist. Daher sind sowohl das Aufbewahren großer Objekte als auch der Versuch, diese für langlebige Objekte zu verwenden, schlechte Ideen.
Den Stack vollständig zu entfernen, um eine Programmiersprache zu vereinfachen, ist der falsche Weg. IMO - ein besserer Ansatz wäre, die Unterschiede zu abstrahieren und den Compiler herausfinden zu lassen, welche Art von Speicher verwendet werden soll, während der Programmierer höhere Programmiersprachen zusammensetzt. Level-Konstrukte, die der Denkweise der Menschen näher kommen - und in der Tat tun High-Level-Sprachen wie C #, Java, Python usw. genau das. Sie bieten eine nahezu identische Syntax für Heap-zugewiesene Objekte und Stack-zugewiesene Primitive ('Referenztypen' vs. 'Werttypen' in .NET Lingo), entweder vollständig transparent oder mit einigen funktionalen Unterschieden, die Sie verstehen müssen, um die Sprache zu verwenden richtig (aber Sie müssen eigentlich nicht wissen, wie ein Stack und ein Heap intern funktionieren).
quelle
Einfach ausgedrückt, ein Stack ist kein bisschen Leistung. Es ist hunderte oder tausende Male schneller als der Haufen. Darüber hinaus bieten die meisten modernen Computer Hardware-Unterstützung für den Stack (wie x86), und die Hardwarefunktionalität für z. B. den Aufruf-Stack kann nicht entfernt werden.
quelle
Nein
Der Stapelbereich in C ++ ist im Vergleich unglaublich schnell. Ich gehe davon aus, dass erfahrene C ++ - Entwickler diese Funktionalität nicht deaktivieren können.
Mit C ++ haben Sie die Wahl und Sie haben die Kontrolle. Die Designer waren nicht besonders geneigt, Funktionen einzuführen, die die Ausführungszeit oder den Ausführungsraum erheblich verlängerten.
Diese Wahl treffen
Wenn Sie eine Bibliothek oder ein Programm erstellen möchten, für das jedes Objekt dynamisch zugewiesen werden muss, können Sie dies mit C ++ tun. Es würde relativ langsam ablaufen, aber Sie könnten dann diese "Modularität" haben. Für den Rest von uns ist die Modularität immer optional. Führen Sie sie nach Bedarf ein, da beide für eine gute / schnelle Implementierung erforderlich sind.
Alternativen
Es gibt andere Sprachen, die erfordern, dass der Speicher für jedes Objekt auf dem Heap erstellt wird. es ist ziemlich langsam, so dass es Designs (reale Programme) auf eine Weise kompromittiert, die schlimmer ist als beides lernen zu müssen (IMO).
Beides ist wichtig, und mit C ++ können Sie beide Funktionen für jedes Szenario effektiv nutzen. Trotzdem ist die C ++ - Sprache möglicherweise nicht ideal für Ihr Design, wenn diese Faktoren in Ihrem OP für Sie wichtig sind (lesen Sie beispielsweise in höheren Sprachen nach).
quelle
Eigentlich dürfte der Leistungseinbruch beachtlich sein!
Wie andere betont haben, handelt es sich bei Stacks um eine äußerst effiziente Struktur für die Verwaltung von Daten, die den LIFO-Regeln (Last-In-First-Out) entsprechen. Die Speicherzuweisung / -freigabe auf dem Stapel ist normalerweise nur eine Änderung an einem Register in der CPU. Das Ändern eines Registers ist fast immer eine der schnellsten Operationen, die ein Prozessor ausführen kann.
Der Heap ist normalerweise eine ziemlich komplexe Datenstruktur, und das Zuweisen / Freigeben von Speicher erfordert viele Anweisungen, um die gesamte zugehörige Buchhaltung durchzuführen. Schlimmer noch, in allgemeinen Implementierungen kann jeder Aufruf zur Arbeit mit dem Heap zu einem Aufruf des Betriebssystems führen. Betriebssystemaufrufe sind sehr zeitaufwändig! Das Programm muss normalerweise vom Benutzermodus in den Kernelmodus wechseln. In diesem Fall kann das Betriebssystem entscheiden, dass andere Programme dringendere Anforderungen haben und dass Ihr Programm warten muss.
quelle
Simula benutzte den Haufen für alles. Alles auf dem Heap Putting induziert immer noch ein Dereferenzierungsebene für lokale Variablen, und es bringt zusätzlichen Druck auf den Garbage Collector (man muss berücksichtigen , dass Garbage Collectors wirklich damals gesaugt). Das ist auch der Grund, warum Bjarne C ++ erfunden hat.
quelle
Stapel sind äußerst effizient für LIFO-Daten, wie z. B. die mit Funktionsaufrufen verbundenen Metadaten. Der Stack nutzt auch die inhärenten Designmerkmale der CPU. Da die Leistung auf dieser Ebene für fast alles andere in einem Prozess von grundlegender Bedeutung ist, wird sich die Annahme dieses "kleinen" Treffers auf dieser Ebene sehr weit verbreiten. Darüber hinaus kann der Heapspeicher vom Betriebssystem verschoben werden, was für Stacks tödlich wäre. Während ein Stapel im Heap implementiert werden kann, ist ein Overhead erforderlich, der buchstäblich jeden Teil eines Prozesses auf der detailliertesten Ebene betrifft.
quelle
"effizient" in Bezug auf das Schreiben von Code, aber sicherlich nicht in Bezug auf die Effizienz Ihrer Software. Die Stapelzuweisungen sind im Wesentlichen frei (es sind nur wenige Maschinenanweisungen erforderlich, um den Stapelzeiger zu verschieben und Platz auf dem Stapel für lokale Variablen zu reservieren).
Da die Stapelzuweisung fast keine Zeit in Anspruch nimmt, ist eine Zuweisung selbst auf einem sehr effizienten Heap 100 KB (wenn nicht mehr als 1 MB) Mal langsamer.
Stellen Sie sich nun vor, wie viele lokale Variablen und andere Datenstrukturen eine typische Anwendung verwendet. Jedes einzelne kleine "i", das Sie als Schleifenzähler verwenden, wird millionenfach langsamer zugeordnet.
Sicher, wenn die Hardware schnell genug ist, können Sie eine Anwendung schreiben, die nur Heap verwendet. Aber stellen Sie sich jetzt vor, welche Art von Anwendung Sie schreiben könnten, wenn Sie Heap nutzen und dieselbe Hardware verwenden würden.
quelle
Möglicherweise interessieren Sie sich für "Garbage Collection ist schnell, aber ein Stapel ist schneller".
http://dspace.mit.edu/bitstream/handle/1721.1/6622/AIM-1462.ps.Z
Wenn ich es richtig lese, haben diese Leute einen C-Compiler modifiziert, um "Stack-Frames" auf dem Heap zuzuweisen, und dann die Garbage Collection verwendet, um die Zuordnung der Frames aufzuheben, anstatt den Stack zu löschen.
Stack-zugewiesene "Stack-Frames" übertreffen die Heap-zugewiesenen "Stack-Frames" entscheidend.
quelle
Wie wird der Aufrufstapel auf einem Haufen funktionieren? Im Wesentlichen müssten Sie in jedem Programm einen Stapel auf dem Heap zuweisen. Warum sollte die OS + -Hardware das nicht für Sie tun?
Wenn Sie möchten, dass die Dinge wirklich einfach und effizient sind, geben Sie dem Benutzer einfach einen Teil des Arbeitsspeichers und lassen Sie ihn damit umgehen. Natürlich will niemand alles selbst implementieren und deshalb haben wir einen Stack und einen Haufen.
quelle
Sowohl Stack als auch Heap sind erforderlich. Sie werden in verschiedenen Situationen verwendet, zum Beispiel:
Grundsätzlich sind die Mechanismen gar nicht vergleichbar, weil so viele Details unterschiedlich sind. Das einzige, was sie gemeinsam haben, ist, dass sie beide irgendwie mit dem Gedächtnis umgehen.
quelle
Moderne Computer verfügen neben einem großen, aber langsamen Hauptspeichersystem über mehrere Cache-Speicherschichten. In der Zeit, die zum Lesen oder Schreiben eines Bytes aus dem Hauptspeichersystem erforderlich ist, können Dutzende Zugriffe auf den schnellsten Cache-Speicher ausgeführt werden. Der tausendfache Zugriff auf einen Standort ist somit viel schneller als der einmalige Zugriff auf 1.000 (oder sogar 100) unabhängige Standorte. Da die meisten Anwendungen wiederholt kleine Speichermengen in der Nähe des oberen Bereichs des Stapels zuweisen und freigeben, werden die Positionen auf dem oberen Bereich des Stapels in enormen Mengen verwendet und wiederverwendet, so dass die überwiegende Mehrheit (99% + in einer typischen Anwendung) Stapelzugriffe können über den Cache-Speicher abgewickelt werden.
Im Gegensatz dazu müsste, wenn eine Anwendung wiederholt Heap-Objekte zum Speichern von Fortsetzungsinformationen erstellen und aufgeben würde, jede Version jedes jemals erstellten Stack-Objekts in den Hauptspeicher geschrieben werden. Selbst wenn die überwiegende Mehrheit solcher Objekte zu dem Zeitpunkt, an dem die CPU die Cache-Seiten, in denen sie begonnen hatten, recyceln wollte, völlig unbrauchbar wäre, hätte die CPU keine Möglichkeit, dies zu wissen. Infolgedessen müsste die CPU viel Zeit damit verschwenden, langsame Speicherschreibvorgänge mit nutzlosen Informationen durchzuführen. Nicht gerade ein Rezept für Geschwindigkeit.
In vielen Fällen ist es hilfreich zu wissen, dass eine an eine Routine übergebene Objektreferenz nach dem Beenden der Routine nicht mehr verwendet wird. Wenn Parameter und lokale Variablen über den Stapel übergeben werden und eine Überprüfung des Codes der Routine ergibt, dass keine Kopie der übergebenen Referenz erhalten bleibt, kann der Code, der die Routine aufruft, sicher sein, dass keine externe Referenz auf die Routine vorhanden ist Objekt existierte vor dem Aufruf, danach existiert kein Objekt mehr. Wenn hingegen Parameter über Heap-Objekte übergeben würden, würden Konzepte wie "Nach Rückkehr einer Routine" etwas nebulöser, da die Routine, wenn der Code eine Kopie der Fortsetzung aufbewahrt, nach a mehr als einmal "zurückkehren" könnte einzelner Anruf.
quelle