Was ist schneller: Stack-Allokation oder Heap-Zuweisung
Diese Frage klingt ziemlich elementar, aber dies ist eine Debatte, die ich hatte, mit einem anderen Entwickler mit denen ich arbeite.
Ich kümmerte mich um Sie zu stapeln zuordnen, Dinge, wo ich könnte, stattdessen heap-Aufteilung. Er Sprach zu mir und gucken mir über die Schulter und bemerkte, dass es nicht nötig war, weil Sie die gleiche Leistung Weise.
Ich hatte immer den Eindruck, dass wachsende Stapel lief konstanter Zeit, und heap-Allokation ist die Leistung abhängig von der aktuellen Komplexität von heap für Zuweisung (Suche nach ein Loch der richtigen Größe) und de-Zuteilung (Einstürzende Löcher, die Fragmentierung zu verringern, da viele standard-Bibliothek Implementierungen nehmen Sie sich Zeit, dies zu tun, während löscht, wenn ich mich nicht Irre).
Dies erscheint mir als etwas, das wäre wahrscheinlich sehr compiler-abhängig. Für dieses Projekt insbesondere bin ich mit einem Metrowerks compiler für die PPC Architektur. Einblick auf diese Kombination wäre sehr hilfreich, aber im Allgemeinen, für GCC und MSVC++, was ist der Fall? Ist heap-Zuweisung nicht so performant wie stack-Zuweisung? Gibt es da keinen Unterschied? Oder sind die Unterschiede so winzig es wird sinnlos Mikro-Optimierung.
Ihre cow-orker ist schrecklich unwissend, aber noch wichtiger ist, er ist gefährlich, weil er macht, autoritative Behauptungen über die Dinge, die er furchtbar unwissend über. Verbrauchsteuern solche Leute aus Ihrem team so schnell wie möglich.
Beachten Sie, dass der heap ist in der Regel viel größer als der stack. Wenn Sie zugeteilt sind, große Mengen von Daten, die Sie wirklich haben, um es über den Haufen, sonst ändern Sie die stack-Größe, die aus der OS.
Alle Optimierungen sind, es sei denn, Sie haben benchmarks oder Komplexität Argumente, die beweisen, ansonsten, standardmäßig sinnlos micro-Optimierungen.
Ich Frage mich, ob Ihr Mitarbeiter hat meist Java oder C# Erfahrung. In jenen Sprachen, fast alles wird auf dem heap zugewiesen unter der Haube, die könnte führen zu solchen Annahmen.
InformationsquelleAutor Adam | 2008-10-02
Du musst angemeldet sein, um einen Kommentar abzugeben.
Stack-Allokation ist viel schneller, weil alles, was es wirklich tut, ist, bewegen Sie den stack-pointer.
Mit Speicher-pools, die Sie bekommen können eine vergleichbare Leistung aus dem heap, aber das kommt mit einer geringen Komplexität und seine eigenen Kopfschmerzen.
Auch, stack vs. heap ist nicht nur ein performance-Aspekt; darüber hinaus erfahren Sie eine Menge über die erwartete Lebensdauer der Objekte.
Auf einigen (meist eingebettet, von denen ich wüsste) - Architekturen, stack gespeichert werden dürfen, schnelle on-Chip-Speicher (z.B. SRAM). Dies kann einen großen Unterschied machen!
Da der stack ist ein Stapel. Sie können nicht frei ein Stück von Speicher vom stack, es sei denn, es ist oben drauf. Es gibt keine management -, push-oder pop-Sachen. Auf der anderen Seite, der heap-Speicher verwaltet wird: es fragt der kernel-Speicher-chunks, vielleicht teilt Sie auf, verschmilzt thems, verwendet Sie und gibt Sie frei. Der Stapel ist wirklich bedeutete für die schnellen und kurzen Zuweisungen.
Da der Stack ist viel kleiner als der Heap. Wenn Sie zuweisen wollen großen arrays, die man besser zuordnen, auf dem Heap. Wenn Sie versuchen zu vergeben, große Arrays auf dem Stack, es würde Ihnen ein Stack-Überlauf. Versuchen Sie es zum Beispiel in C++: int t[100000000]; Versuchen Sie es zum Beispiel t[10000000] = 10; und dann cout << t[10000000]; Es sollte Ihnen ein stack-überlauf oder einfach nicht funktioniert und nicht zeigen, dass Sie nichts. Aber wenn Sie zuweisen, das array auf dem heap: int *t = new int[100000000]; und do die gleichen Aktivitäten nach, es wird funktionieren, weil der Heap hat die notwendige Größe für ein solch großes array.
Der offensichtlichste Grund ist, dass die Objekte auf den stack gehen, out of scope, die beim verlassen des Blocks, die Sie zugeordnet sind.
InformationsquelleAutor Torbjörn Gyllebring
Stack ist viel schneller. Es ist buchstäblich nur verwendet eine einzelne Anweisung auf den meisten Architekturen, die in den meisten Fällen, z.B. auf x86:
(, Bewegt sich der stack-pointer nach unten durch 0x10 Byte und damit "reserviert" diejenigen bytes, die für die Verwendung durch eine variable.)
Natürlich, die stack-Größe ist sehr, sehr begrenzt, da du schnell herausfinden, wenn Sie übernutzung stack allocation oder versuchen zu tun Rekursion 🙂
Auch, es gibt wenig Grund für die performance-Optimierung des Codes, die nicht nachweislich Bedarf es, wie gezeigt, durch die Erstellung von Profilen. "Premature optimization" führt oft zu mehr Probleme als es Wert ist.
Meine Faustregel: wenn ich weiß, brauche ich noch einige Daten zur compile-Zeit, und es ist unter ein paar hundert bytes groß, ich stack-Speicher. Ich sonst heap-Speicher.
Den Punkt getroffen, besonders der Punkt über nachweislich, die Sie benötigen. Ich bin ständig erstaunt, wie Menschen, die Bedenken bezüglich der Leistung sind fehl am Platz.
"Freigabe" ist auch sehr einfach und erfolgt mit einer einzigen
leave
Unterricht.Beachten Sie die "versteckten" Kosten hier, vor allem für die erste Zeit, die Sie erweitern den stack. Dies könnte Ergebnis in a page fault, eine Kontextumschaltung zu dem kernel, der muss einige Arbeit, um den Speicher(oder laden Sie es aus tauschen, im schlimmsten Fall).
In einigen Fällen können Sie sogar reservieren Sie mit 0 Hinweise. Wenn einige Informationen darüber bekannt ist, wie viele bytes müssen zugeordnet werden, kann der compiler reservieren Sie im Voraus, gleichzeitig weist es andere stack-Variablen. In diesen Fällen zahlen Sie gar nichts!
InformationsquelleAutor Dan Lenski
Ehrlich gesagt, es ist trivial, ein Programm zu schreiben vergleichen Sie die Leistung:
Es wird gesagt, dass ein törichter Konsistenz ist der Kobold der kleinen Geister. Offenbar Optimierung Compiler sind die hobgoblins von vielen Programmierer-Köpfen. Diese Diskussion genutzt werden, unten die Antwort, aber die Leute anscheinend nicht die Mühe gemacht, zu Lesen, dass weit, so ziehe ich es bis hier, zu vermeiden, immer Fragen die ich schon beantwortet.
Ein optimierender compiler feststellen, dass dieser code tut nichts, und optimieren Sie alle Weg. Es ist der optimizer die Arbeit zu tun Sachen wie, dass, und die Bekämpfung der optimizer ist ein Narr Botengang.
Ich würde empfehlen, kompilieren Sie den code mit Optimierung ausgeschaltet, weil es keine gute Weise, zu täuschen, die jeden Optimierer, die derzeit in Gebrauch oder den Gebrauch in der Zukunft.
Wer schaltet den Optimierer an und meckert dann über den Kampf sollte es werden, das Gegenstand der öffentlichen Lächerlichkeit preisgegeben.
Wenn ich kümmerte Nanosekunden-Präzision würde ich nicht verwenden
std::clock()
. Wenn ich wollte, die Veröffentlichung der Ergebnisse als Doktorarbeit würde ich eine größere Sache über dieses, und ich würde wahrscheinlich zu vergleichen, GCC, Tendra/Ten15, LLVM, Watcom, Borland, Visual C++, Digital Mars, ICC und andere Compiler. Wie es ist, heap-Zuweisung dauert hundert mal länger als stack-Zuweisung, und ich sehe nichts nützliches über die Untersuchung der Frage nicht weiter.Der Optimierer hat eine mission, um loszuwerden, der code, den ich Teste. Ich sehe keinen Grund dafür, zu sagen, dass der Optimierer ausführen und dann versuchen zu täuschen, die der Optimierer in eigentlich nicht optimieren. Aber wenn ich sah, mit dem Wert zu tun, die ich tun würde, eine oder mehrere der folgenden:
Hinzufügen eines Daten-Mitglieds auf
empty
, und auf die Daten zugreifen, Mitglied in der Schleife, aber wenn ich immer nur Lesen von Daten, die Mitglied der optimizer tun können, constant folding und entfernen Sie die Schleife, wenn ich immer nur schreiben, um die Daten-member, kann der Optimierer überspringen Sie alle, aber die Letzte iteration der Schleife. Außerdem, die Frage war nicht "stack Allokation und data access vs. heap und den Zugriff auf Daten."Erklären
e
volatile
, abervolatile
wird oft falsch zusammengestellt (PDF).Nehmen Sie sich die Adresse von
e
innerhalb der Schleife (und vielleicht weisen Sie auf eine variable, die deklariertextern
und in einer anderen Datei definiert). Aber selbst in diesem Fall kann der compiler feststellen, dass -- auf dem Stapel mindestens --e
wird immer zugewiesen wird, den gleichen Speicher-Adresse, und führen Sie dann das constant folding wie in (1) oben. Ich bekomme alle Iterationen der Schleife, aber das Objekt ist eigentlich nie vergeben.Neben dem offensichtlichen, ist dieser test fehlerhaft, dass es Maßnahmen Zuweisung und-Freigabe, und die ursprüngliche Frage gar nicht zu Fragen, über die Freigabe. Natürlich Variablen auf dem Stapel reserviert werden automatisch freigegeben, am Ende Ihrer Reichweite, also ruft nicht
delete
würde (1) verzerren die zahlen (Stapel-Freigabe ist im Lieferumfang enthalten in den zahlen über die stack-Zuweisung, so ist es nur fair zu Messen heap deallocation) und (2) bewirken ein ziemlich schlechtes Gedächtnis Auslaufen, es sei denn, wir halten eine Referenz auf das neue Zeiger und rufendelete
nach wir haben unsere Zeit-Messung.Auf meinem Rechner mit g++ 3.4.4 auf Windows, bekomme ich "0 Zeiteinheiten" für beide stack-und heap-Zuweisung für etwas weniger als 100000 Zuweisungen und selbst dann bekomme ich "0 Zeiteinheiten" für stack-Allokation und die "15-Uhr-ticks" für heap-allocation. Wenn ich Messen 10,000,000 Zuweisungen, stack Allokation dauert 31 Uhr tickt und heap-Zuweisung dauert 1562 Uhr tickt.
Ja, ein optimierender compiler kann elide erstellen der leeren Objekten. Wenn ich das richtig verstehe, kann es auch umgehen, aber die ganze erste Schleife. Wenn ich bis stieß der Iterationen, die bis zu 10.000.000 stack Allokation nahmen 31 Uhr tickt und heap nahm 1562 Uhr tickt. Ich denke, es ist sicher zu sagen, dass ohne Rücksprache mit g++ zu optimieren, die ausführbare Datei g++ nicht umgehen, aber die Konstruktoren.
In den Jahren, seit ich dies schrieb, ist die Einstellung auf Stack Overflow wurde, um post-performance von optimierten builds. Im Allgemeinen, ich denke, das ist richtig. Aber ich denke immer noch, es ist dumm zu Fragen, der compiler optimiert code, wenn Sie in der Tat nicht wollen, dass die code optimiert. Es scheint mir sehr ähnlich zu zahlen extra für valet-Parkplatz, aber die Verweigerung der hand über die Tasten. In diesem speziellen Fall, will ich nicht den optimizer laufen.
Verwenden eine leicht modifizierte version von der benchmark (um die Adresse der gültigen Punkt, dass das ursprüngliche Programm nicht zuweisen, die etwas auf den stack jedes mal durch die Schleife) und kompilieren ohne Optimierungen, aber die Verknüpfung zur Freigabe von Bibliotheken (Adresse die gültigen Punkt, dass wir nicht wollen, auch eine Verlangsamung verursacht durch die Verknüpfung der debug-Bibliotheken):
zeigt:
auf meinem system beim kompilieren mit der Befehlszeile
cl foo.cc /Od /MT /EHsc
.Können Sie nicht einverstanden mit meinem Ansatz zu einem nicht-optimierten bauen. Das ist in Ordnung: fühlen Sie sich frei ändern Sie den Maßstab so viel wie Sie wollen. Wenn ich auf Optimierung, bekomme ich:
Nicht, weil stack-Allokation ist eigentlich augenblicklich, sondern weil jede halbwegs anständige compiler feststellen können, dass
on_stack
nichts nützliches und optimiert werden kann entfernt. GCC auf meinem Linux-laptop auch bemerkt, dasson_heap
nichts nützliches, und optimiert es Weg, wie gut:Ich bin auch froh, dass die Erhöhung der Anzahl, wie oft jede option loop läuft (plus anweisen g++ nicht zu optimieren?) hat zu bedeutenden Ergebnissen geführt. So, jetzt haben wir harte Fakten zu sagen-stack ist schneller. Vielen Dank für Ihre Bemühungen!
Es ist der Optimierer die Aufgabe, um loszuwerden, code wie diesen. Gibt es einen guten Grund, sich zu drehen, die der Optimierer auf und dann verhindern, dass es tatsächlich optimieren? Ich habe bearbeitet die Antwort auf die Dinge noch klarer: wenn Sie genießen Kampf der Optimierer, bereit sein, zu erfahren, wie smart-compiler-Autoren.
Ich bin sehr spät, aber es ist auch sehr erwähnenswert, hier, dass die heap-Allokation-Anforderungen-Speicher durch den kernel, also die Leistung hat auch stark abhängig von der Effizienz des Kernels. Mit diesem code, mit Linux (Linux 3.10.7-gentoo #2 SMP Wed Sep 4 18:58:21 MDT 2013 x86_64), modifizieren, für die HR-timer, und über 100 Millionen Iterationen pro Schleifendurchlauf ergibt diese performance:
stack allocation took 0.15354 seconds, heap allocation took 0.834044 seconds
mit-O0
gesetzt, so dass Linux-heap nur langsamer auf einen Faktor von etwa 5,5 auf meine Maschine.Auf windows ohne Optimierungen (debug build) wird es mit der debug-heap, die ist viel langsamer als die nicht-debug-heap. Ich glaube nicht, dass es eine schlechte Idee, um "trick" die optimizer an alle. Compiler-Schreiber sind intelligent, aber Compiler sind nicht AI ist.
InformationsquelleAutor
Eine interessante Sache, die ich gelernt, über Stack vs. Heap-Zuweisung auf der Xbox 360 Xenon-Prozessor, der möglicherweise auch für andere von multicore-Systemen ist, dass die Zuteilung auf dem Heap verursacht einen Kritischen Abschnitt betreten werden halt alle anderen Kerne so, dass der alloc keinen Konflikt. So, in einer engen Schleife, Stack Allokation war der Weg zu gehen für die Feste Größe von arrays als es verhindert Stände.
Dies könnte eine weitere Beschleunigung zu berücksichtigen, wenn Sie Sie Kodieren für multicore/multiproc, dass Ihr stack Allokation werden nur sichtbar durch den Kern läuft Ihre Gültigkeitsbereich der Funktion, und das wird sich nicht auf andere Kerne/CPUs.
Das ist ein Effekt, der die (vor allem schlechte) Implementierung des heap-allocator. Besser heap allocators müssen erwerben nicht eine Sperre für jede Zuordnung.
InformationsquelleAutor Furious Coder
Schreiben Sie einen speziellen heap-Zuweisung für bestimmte Größen von Objekten, die sehr performant arbeitet. Jedoch, die Allgemeinen heap-allocator ist nicht besonders performant.
Ich Stimme mit Torbjörn Gyllebring über die erwartete Lebensdauer der Objekte. Guter Punkt!
InformationsquelleAutor Chris Jester-Young
Ich glaube nicht, dass stack-heap-Allokation und Zuweisung sind in der Regel austauschbar sind. Ich hoffe auch, dass die Leistung der beiden ist ausreichend für den Allgemeinen Gebrauch.
Würde ich dringend empfehlen, für kleine Gegenstände, je nachdem, welche ist besser geeignet, um den Umfang der Zuweisung. Für große Gegenstände, der Haufen ist wohl nötig.
Auf 32-bit-Betriebssysteme, die mehrere threads, stack ist oft begrenzt (wenn auch in der Regel für mindestens ein paar mb), weil der Adressraum muss geschnitzt werden und früher oder später ein thread-stack läuft in die andere. Auf single-threaded-Systeme (Linux-glibc single threaded sowieso) die Begrenzung ist viel kleiner, weil der stack kann nur wachsen und wachsen.
Auf 64-bit-Betriebssystemen gibt es genügend Adressraum, um thread-stacks sehr groß.
InformationsquelleAutor MarkR
In der Regel stapelzuweisung nur aus der Subtraktion von der stack-pointer-register. Dies ist Tonnen schneller als die Suche in einem heap.
Manchmal stack Allokation erfordert das hinzufügen einer Seite(s) des virtuellen Speichers. Hinzufügen einer neuen Seite eingeschossen Speicher nicht benötigen, Lesen Sie eine Seite von der Festplatte, so dass in der Regel dies ist immer noch Tonnen schneller als die Suche in einem heap (vor allem, wenn ein Teil des heap wurde ausgelagert zu haben). In einer seltenen situation, und Sie bauen könnten, so ein Beispiel, genug Platz nur gerade zufällig zur Verfügung steht, die in einem Teil der heap, die bereits im RAM sind, aber die Zuweisung einer neuen Seite, für die der stack hat, warten Sie, bis die andere Seite auf die Festplatte geschrieben. In dieser seltenen situation, wird der heap schneller.
Hier ist ein Beispiel. Das aufrufende Programm fragt reservieren 37 bytes. Die Bibliothek-Funktion sucht nach einem block, der mindestens 40 bytes. Der erste block auf die freie Liste hat 16 Byte. Den zweiten block auf der freien Liste hat 12 bytes. Der Dritte block hat 44 Byte. Die Bibliothek Stoppt die Suche an diesem Punkt.
InformationsquelleAutor Windows programmer
Abgesehen von den Größenordnungen performance-Vorteil gegenüber heap, stack-Allokation ist vorzuziehen, für lang laufende server-Anwendungen. Selbst die am besten verwalteten heaps irgendwann so fragmentiert, dass die Leistung der Anwendung beeinträchtigt.
InformationsquelleAutor Jay
Einem Stapel hat eine begrenzte Kapazität, während ein heap ist nicht. Die typischen stack für einen Prozess oder thread ist um 8K. Sie können nicht ändern die Größe, wenn es einmal zugeordnet.
Eine stack-variable folgt die scoping-Regeln, während ein Haufen nicht. Wenn Ihr instruction pointer geht über eine Funktion, alle neuen Variablen im Zusammenhang mit der Funktion Weg.
Wichtigsten von allen, können Sie nicht Vorhersagen, die die gesamte Funktion aufrufen, Kette im Voraus. Also nur etwa 200 bytes Zuteilung auf Ihren Teil auslösen kann, ist ein stack-überlauf. Dies ist besonders wichtig, wenn Sie schreiben eine Bibliothek, nicht eine Anwendung.
Auf meiner Maschine ist die Standard-stack-Größe für einen Prozess ist 8 MB, nicht kB. Wie alt ist dein computer?
Es war ein Handy.
InformationsquelleAutor yogman
Ich denke, die Lebensdauer ist entscheidend, ob die Sache zugewiesen ist, konstruiert werden, die in komplexer Weise. Zum Beispiel, in der transaction-driven-modeling, haben Sie in der Regel zum ausfüllen und übergeben der Struktur der Transaktion mit einer Reihe von Feldern zur Bedienung der Funktionen. Blick auf die OSCI-SystemC-TLM-2.0-standard für ein Beispiel.
Zuweisung dieser auf die Stapel in der Nähe der Aufruf der operation neigt dazu, die enormen Aufwand, denn die Konstruktion ist teuer. Der gute Weg ist, zu reservieren über den Haufen und verwenden Sie die Transaktion Objekte entweder durch Bündelung oder eine einfache Richtlinie wie "dieses Modul muss nur eine Transaktion ein Objekt überhaupt".
Dies ist viele Male schneller als die Zuweisung der Objekt-auf jedem Betrieb nennen.
Der Grund ist einfach, dass das Objekt verfügt über eine aufwendige Konstruktion und eine relativ lange Lebensdauer.
Ich würde sagen: probieren Sie beide und sehen, was funktioniert am besten in Ihrem Fall, denn es kann wirklich angewiesen auf das Verhalten Ihres Codes.
InformationsquelleAutor jakobengblom2
Wohl das größte problem der heap vs. stack-Allokation ist, dass heap im Allgemeinen Fall ist ein unbounded Betrieb, und so können Sie es nicht verwenden, wo timing ist ein Problem.
Für andere Anwendungen wo timing ist nicht ein Problem, es mag nicht wie viel, aber wenn man heap wählen viel, wird sich dies auf die Ausführungsgeschwindigkeit. Immer versuchen, den Stapel von kurzer Dauer und oft den allokierten Speicher (z.B. in Schleifen), und so lange wie möglich tun heap-Zuweisung während des Starts der Anwendung.
InformationsquelleAutor larsivi
Es ist nicht jsut stack-Zuweisung, die ist schneller. Gewinnen Sie auch eine Menge über die Verwendung von stack-Variablen. Sie haben eine bessere Lokalität der Referenz. Und schließlich die Freigabe ist viel billiger zu.
InformationsquelleAutor MSalters
Stack Allokation wird fast immer als schnell oder schneller als heap-Zuweisung, obwohl es sicherlich möglich, einen heap-Zuweisung verwenden Sie einfach einen stack-basierten Allokation Technik.
Jedoch, gibt es größere Probleme beim Umgang mit der performance-stack vs. heap-basierten Zuteilung (oder in etwas bessere Bedingungen, lokale oder externe Vergabe). In der Regel, heap (extern) Zuweisung ist langsam, weil es ist der Umgang mit vielen verschiedenen Arten von Zuweisungen und-Allokation-Muster. Eine Reduzierung des Umfangs der Zuweisung, die Sie verwenden (so dass es lokal auf dem Algorithmus/code) dazu neigen wird, um die Leistung zu erhöhen ohne wesentliche Veränderungen. Hinzufügen bessere Struktur, Ihre Aufteilung Muster, zum Beispiel, zwingt eine LIFO-Bestellung über die Zuteilung und Freigabe-Paare können auch verbessern Sie Ihre allocator performance durch die Verwendung der Zuweisung in einfacher und strukturierter Weise. Oder Sie verwenden können, oder schreiben Sie eine Zuweisung abgestimmt, für Ihre jeweilige Zuweisung Muster; die meisten Programme weisen ein paar diskrete Größen Häufig, so dass ein heap basiert auf einem lookaside buffer von ein paar Feste (möglichst bekannten) Größen führen sehr gut. Windows verwendet die low-fragmentation-heap genau aus diesem Grund.
Auf der anderen Seite, stack-basierte Allokation auf einem 32-bit-Speicherbereich ist auch voller Gefahren, wenn Sie zu viele threads. Die Stapel müssen einen zusammenhängenden Speicherbereich, so dass die mehr threads du hast, desto mehr virtuelle Adresse Speicherplatz Sie benötigen, für Sie zu laufen ohne einen stack-überlauf. Dies wird nicht ein problem sein (für jetzt) mit 64-bit, aber es kann sicherlich verheerenden Schaden anrichten in lange Laufenden Programme mit vielen threads. Der virtuelle Adressraum aufgrund der Fragmentierung ist immer ein Schmerz zu behandeln.
InformationsquelleAutor MSN
Stack-Allokation ist ein paar Anweisungen in der Erwägung, dass die Schnellste rtos-heap-allocator mir bekannt (TLSF) verwendet, im Durchschnitt in der Größenordnung von 150 Anweisungen. Auch stack Zuweisungen nicht benötigen eine Sperre, weil Sie verwenden thread local storage ist eine weitere große Leistung gewinnen. So stack-Allokationen können sich 2-3 Größenordnungen schneller, je nachdem, wie stark Multithread-Umgebung.
Im Allgemeinen heap letzten Ausweg, wenn Sie sich über Leistung. Ein tragfähiges in-zwischen option kann ein fester pool-Zuweisung, die auch nur ein paar Anweisungen und hat sehr wenig pro-allocation-overhead, so ist es ideal für kleine Feste Größe der Objekte. Auf der Sollseite steht, es funktioniert nur mit fester Größe, Objekte, die nicht von sich aus thread-sicher und hat den block Fragmentierung Probleme.
InformationsquelleAutor Andrei Pokrovsky
Gibt es eine Allgemeine Aussage darüber getroffen werden, solche Optimierungen.
Die Optimierung, die Sie bekommen, ist proportional zur Menge der Zeit, die das Programm counter ist eigentlich im code.
Wenn Sie sample program counter, finden Sie heraus, wo er verbringt seine Zeit, und das ist in der Regel in einem kleinen Teil des Codes, und oft in der Bibliothek Routinen, die Sie haben keine Kontrolle über.
Nur, wenn Sie es finden, verbrachte viel Zeit in der heap-Allokation und Ihrer Objekte wird es merklich schneller, Stapel-zuordnen.
InformationsquelleAutor Mike Dunlavey
Wie schon andere gesagt haben, stack-Allokation ist in der Regel viel schneller.
Allerdings, wenn Sie Ihre Objekte sind aufwändig zu kopieren, die Zuordnung der Stapel führen zu einer enormen performance-Treffer später, wenn Sie die Objekte verwenden, wenn Sie nicht vorsichtig sind.
Zum Beispiel, wenn Sie etwas reservieren auf dem stack, und dann legen Sie Sie in einem container, es wäre besser gewesen, zu reservieren auf dem heap, und speichern Sie den Zeiger im container (z.B. mit std::shared_ptr<>). Das gleiche gilt, wenn Sie auf der Durchreise sind oder wieder Objekte von Wert, und ähnliche Szenarien.
Der Punkt ist, dass obwohl der stack-Allokation ist in der Regel besser als heap-Allokation in vielen Fällen, manchmal, wenn Sie gehen aus dem Weg zu Stapel zuweisen, wenn es nicht die best-fit-das Modell der Berechnung, es kann mehr Probleme verursachen als es löst.
InformationsquelleAutor wjl
Wäre es, wie dies in asm. Wenn Sie in
func
, dief1
und Zeigerf2
zugewiesen wurde, wurde auf den stack (automatische Speicherung). Und durch die Art und Weise, Foof1(a1)
hat keine Anweisung Auswirkungen auf stack-pointer (esp
),Es hat gewesen zugeteilt, wennfunc
will das Mitgliedf1
es die Anweisung ist so etwas wie dieses:lea ecx [ebp+f1], call Foo::SomeFunc()
. Eine andere Sache, die stack-Zuweisung machen kann jemand denken, das Gedächtnis ist so etwas wieFIFO
, dieFIFO
nur geschehen, wenn Sie gehen in eine Funktion, wenn Sie sind in der Funktion und weisen so etwas wieint i = 0
gibt es keine push passiert ist.InformationsquelleAutor bitnick
Es wurde bereits erwähnt, dass die stack-Allokation ist einfach, bewegen Sie den stack-pointer, das ist, einer einzelnen Anweisung auf den meisten Architekturen. Vergleichen Sie das mit dem, was in der Regel passiert im Fall der heap-allocation.
Das Betriebssystem verwaltet Teile des freien Speichers als eine verknüpfte Liste mit der payload-Daten, die aus der Zeiger auf die Startadresse des freien Teil und die Größe des freien Teil. To allocate X bytes des Arbeitsspeichers, die link-Liste Durchlaufen wird und jeder Hinweis ist, besuchte in der Folge, die überprüfung zu sehen, wenn seine Größe mindestens X. Wenn ein Teil mit der Größe der P >= X gefunden wird, P ist in zwei Teile geteilt mit Größen X und P-X. Die verlinkte Liste ist aktualisiert, und der Zeiger auf das erste Teil zurückgegeben.
Wie Sie sehen können, heap abhängig, können Faktoren wie, wie viel Speicher, die Sie anfordern, wie fragmentiert der Speicher und so weiter.
InformationsquelleAutor Nikhil
Im Allgemeinen, stack-Allokation ist schneller als heap-Zuweisung wie bereits erwähnt bei fast jeder Antwort oben. Ein stack, push und pop O(1), in der Erwägung, dass die Zuweisung oder Befreiung aus einem heap kann verlangen, einen Spaziergang von früheren Zuteilungen. Allerdings sollten Sie nicht in der Regel die Zuweisung in engen, performance-intensiven Schleifen, so dass die Wahl wird in der Regel nach unten kommen, um andere Faktoren.
Könnte es gut sein, um diese Unterscheidung: Sie können eine "stack-Zuweisung" auf dem heap. Streng genommen, ich nehme stack allocation bedeutet, die tatsächliche Methode der allocation, anstatt die Lage der Verteilung. Wenn Sie die Zuordnung einer Menge Sachen, die auf der eigentlichen Programm-stack, das könnte schlecht sein für eine Vielzahl von Gründen. Auf der anderen Seite, verwenden Sie eine Stapel-Methode zum allokieren auf dem heap wenn möglich, ist die beste Wahl, die Sie machen können für eine Verrechnung.
Da Sie erwähnt Metrowerks und PPC, ich vermute, du meinst die Wii. In diesem Fall wird Speicher an einer Prämie ist, und mit einem stack-allocation-Methode, wo immer möglich, gewährleistet, dass Sie nicht verschwenden Speicher auf Fragmente. Natürlich, dies zu tun erfordert viel mehr Pflege als "normale" heap-allocation-Methoden. Es ist klug zu bewerten, die vor-und Nachteile für jede situation.
InformationsquelleAutor Dan Olson
Bemerkung, dass die überlegungen sind in der Regel nicht um Geschwindigkeit und Leistung bei der Auswahl der stack versus heap. Der stack verhält sich wie ein stack, was bedeutet, es ist gut geeignet zum schieben der Blöcke und knallen Sie wieder, die Letzte in, first out. Die Ausführung der Verfahren auch stack-wie, letzten Prozedur eingegeben wird, den ersten zu verlassen. In den meisten Programmiersprachen alle Variablen in einer Prozedur wird nur sichtbar sein, während die Prozedur die Ausführung, damit Sie geschoben werden, beim betreten einer Prozedur und zog aus dem Stapel auf exit oder return.
Nun für ein Beispiel, wo der stack nicht verwendet werden kann:
Wenn Sie reservieren von Speicher im Verfahren S und legte es auf den stack und beenden Sie dann die S, die die zugewiesenen Daten werden spontan aus dem Stapel. Aber die variable x in P auch darauf hingewiesen, dass die Daten, so dass x zeigt jetzt an einen Ort unterhalb der stack-pointer (angenommen stack wächst nach unten) mit einem unbekannten Inhalt. Der Inhalt könnte noch da sein, wenn der stack-pointer wird nur nach oben verschoben, ohne löschen der Daten darunter, aber wenn Sie beginnen, Zuweisung von neuen Daten auf dem stack der Zeiger x könnte in der Tat Punkt, um die neuen Daten statt.
InformationsquelleAutor Kent Munthe Caspersen
Spezifischen Probleme in der Sprache C++
Zunächst es gibt keine sogenannten "stack" oder "heap" - Zuordnung, welche durch C++. Wenn Sie sprechen über die automatische Objekte in den Blockbereich, Sie sind noch nicht "zugeteilt". (BTW, automatische Speicherung Dauer in C ist definitiv NICHT das gleiche, "zugewiesen"; die letztere ist "dynamisch" im C++ - Jargon.) Und die dynamisch zugewiesenen Speicher ist auf der free store, nicht unbedingt auf den "heap", obwohl letzteres ist oft der (Standard -) Umsetzung.
Obwohl gemäß der abstrakten semantischen Regeln, die automatische Objekte noch Speicher belegen, ein konformer C++ - Implementierung erlaubt, um diese Tatsache zu ignorieren, wenn es nachweisen kann, dass dies nicht egal ist (wenn es sich nicht ändern das beobachtbare Verhalten des Programms). Diese Berechtigung ist durch die wie-wenn Regel in ISO C++, die auch der generalklausel ermöglicht die üblichen Optimierungen (und es ist auch eine fast gleiche Regel in ISO-C). Neben der als-ob-Regel ISO-C++ auch die copy elision Regeln, mit denen die Unterlassung von spezifischen Kreationen von Objekten. Der Konstruktor und der Destruktor ruft involviert sind, werden dabei weggelassen. Als Ergebnis, die automatische Objekte (wenn vorhanden) in diese Konstruktoren und Destruktoren sind auch abgeschafft, im Vergleich zum naiv-abstrakte Semantik implizit durch den Quellcode.
Auf der anderen Seite,, Kostenlose store-Aufteilung ist auf jeden Fall "Zuweisung" von design. Unter ISO-C++ - Regeln, eine solche Verteilung kann erreicht werden durch einen Anruf von einer Zuordnung Funktion. Aber seit ISO C++14 gibt es eine neue (nicht-als-wenn) Regel, die dies erlaubt die Zusammenführung global allocation-Funktion (d.h.
::operator new
) fordert in bestimmten Fällen. So teilen die dynamische Zuweisung Operationen können auch no-op wie in dem Fall der automatischen Objekten.Zuweisung Funktionen, Ressourcen Speicher. Objekte können weiter zugeteilt, basierend auf Zuweisung mit allocators. Für die automatische Objekte, Sie sind direkt präsentiert - obwohl der zugrunde liegende Speicher zugegriffen werden kann und verwendet werden, um Speicher, um andere Objekte (durch Platzierung
new
), aber das macht nicht viel Sinn, da der freie Speicher, denn es gibt keine Möglichkeit zum verschieben der Ressourcen anderswo.Alle anderen Bedenken sind außerhalb der Reichweite von C++. Dennoch, können Sie immer noch erheblich.
Über Implementierungen von C++
C++ nicht aussetzen vergegenständlicht Aktivierung Datensätze oder einige Arten von erste-Klasse Fortsetzungen (z.B. durch die berühmte
call/cc
), es gibt keine Möglichkeit, direkt zu manipulieren die Aktivierungs-record-Rahmen - in denen die Umsetzung benötigen, um die automatische Objekte. Einmal gibt es keine (nicht-portable) interoperations mit der zugrunde liegenden Implementierung ("native" nicht-portablen code als inline-Assembler-code), eine Auslassung, die zugrunde liegende Zuordnung der frames werden kann, ziemlich trivial. Zum Beispiel, wenn die aufgerufene Funktion inlined, die frames kann man effektiv zusammengeführt in die andere, so dass es keine Möglichkeit gibt, um zu zeigen, was die "Zuordnung".Jedoch einmal interops geachtet werden, die Dinge werden immer komplexer. Eine typische Implementierung in C++ setzen Sie die Fähigkeit der interop auf ISA (instruction set architecture) mit einigen Aufrufkonventionen als die binären Grenze gemeinsam mit der nativen (ISA-Ebene der Maschinen -) code. Dies wäre explizit zu teuer, insbesondere, wenn die Aufrechterhaltung der stack-pointer, die oft direkt statt, indem eine ISA-level-register (mit wohl spezifische Maschinenbefehle für den Zugriff). Der stack-pointer kennzeichnet die Grenze des oberen Rahmen des (derzeit aktiv) - Funktion aufrufen. Wenn eine Funktion aufgerufen wird eingegeben, wird ein neuer Rahmen benötigt wird, und der stack-pointer addiert oder subtrahiert (abhängig von der convention der ISA) durch einen Wert von nicht weniger als die benötigte Bildgröße. Der Rahmen wird dann gesagt zugeordnet, wenn der stack-pointer nach der Operationen. Parameter von Funktionen dürfen übergeben werden, auf den stack-Rahmen als auch, je nach Aufrufkonvention verwendet für den Aufruf. Die Rahmen können halten Sie die memory-automatische Objekte (wohl einschließlich der Parameter) angegeben, die von den C++ - source-code. In dem Sinne, Implementierungen, diese Objekte sind "reserviert". Wenn die Steuerung beendet den Funktionsaufruf, der Rahmen ist nicht mehr notwendig, es ist in der Regel frei durch die Wiederherstellung der stack-pointer wieder in den Zustand vor dem Aufruf von (zuvor gespeicherten nach der Aufrufkonvention). Dies kann gesehen werden, als "Freigabe". Diese Vorgänge macht der Aktivierungsstatus effektiv eine LIFO-Datenstruktur, so dass es oft als "die (call -) Stapel". Der stack-pointer effektiv zeigt die Obere Lage des Stapels.
Weil die meisten C++ - Implementierungen (vor allem diejenigen, die targeting-ISA-Ebene nativen code und über die assembly-Sprache, die als seine unmittelbare output) nutzt ähnliche Strategien wie diese, so verwirrend "allocation" - Regelung ist beliebt. Solche Zuweisungen (sowie deallocations) verbringen Maschine Zyklen, und es kann teuer werden, wenn die (nicht optimierte) ruft Häufig auftreten, auch wenn moderne CPU microarchitectures können komplexe Optimierungen implementiert in der hardware für die gemeinsame code-Muster (wie mit einem stack engine bei der Umsetzung
PUSH
/POP
Anweisungen).Aber trotzdem, im Allgemeinen, es ist wahr, dass die Kosten für die stack-frame-Zuordnung ist deutlich weniger als ein Anruf, um eine Zuordnung Funktion die Bedienung der free store (es sei denn, es ist völlig wegoptimiert), die sich Hunderte (wenn nicht Millionen 🙂 Operationen zur Aufrechterhaltung der stack-pointer und anderen Staaten. Zuordnung Funktionen sind in der Regel basierend auf API zur Verfügung gestellt, die durch die gehostete Umgebung (z.B. Laufzeit, sofern durch das OS). Verschiedene für die Durchführung von automatischen Objekten für die Funktionen Aufrufe, solche Zuweisungen sind allgemein-vorgesetzt, so dass Sie nicht haben frame-Struktur wie ein stack. Traditionell, Sie Speicherplatz von der pool-Speicher genannt heap (oder mehrere heaps). Anders als die "stack", der Begriff "heap" bedeutet hier nicht an, die Datenstruktur, die verwendet wird; es ist abgeleitet von der frühen Implementierungen von Programmiersprachen, die vor Jahrzehnten. (BTW, ist der call-stack ist in der Regel zugeordnet, die mit festen oder benutzerdefinierten Größe auf dem heap, indem Sie die Umwelt in Programm oder thread starten.) Die Art der Nutzung Fällen macht Zuweisungen und deallocations aus einem heap weit komplizierter (als push-oder pop stack-frames), und kaum möglich zu sein, direkt optimiert durch hardware.
Auswirkungen auf Memory Access
Den üblichen stack-Zuweisung legen Sie immer den neuen Rahmen auf der Oberseite, so hat es eine sehr gute Lokalität. Diese ist freundlich zu cache. OTOH, Speicher zufällig in der free store hat keine solche Eigenschaft. Da die ISO-C++17, es gibt pool-Ressourcen-Vorlagen zur Verfügung gestellt von
<memory>
. Der direkte Zweck dieser Schnittstelle ist es, die Ergebnisse von aufeinander folgenden Zuweisungen nah zusammen im Speicher. Dies bestätigt die Tatsache, dass diese Strategie ist im Allgemeinen gut für die Leistung, die mit modernen Implementierungen, z.B. freundlich zu cache in modernen Architekturen. Dies ist über die Leistung von Zugang eher als Zuordnung, obwohl.Parallelität
Erwartung der gleichzeitige Zugriff der Speicher kann verschiedene Effekte zwischen den stapeln und Haufen. Ein call-stack ist in der Regel ausschließlich im Besitz von einem thread der Ausführung in eine C++ Implementierung. OTOH, die Haufen sind oft gemeinsamen unter den threads in einem Prozess. Für solche Haufen, die Reservierung und Freigabe-Funktionen haben, zum Schutz der gemeinsamen internen Verwaltungs-Daten-Struktur aus Daten Rennen. Als Ergebnis, heap-Zuweisungen und deallocations können zusätzliche overhead durch interne Synchronisation Operationen.
Raum Effizienz
Aufgrund der Natur von Anwendungsfällen und interne Datenstrukturen, heaps kann, leidet unter internen Fragmentierung des Speichers, während der Stapel nicht. Dies hat keine direkten Auswirkungen auf die Leistungsfähigkeit der Speicher-Zuweisung, aber in einem system mit virtueller Speicher, wenig Platz-Effizienz degenerieren die Gesamtleistung des Speicher-Zugriff. Dies ist besonders schrecklich, wenn HDD als swap physischen Speicher. Es kann zu sehr langen Wartezeiten - manchmal Milliarden von Zyklen.
Einschränkungen der Stack Zuweisungen
Stack Zuweisungen sind oft besser in der Leistung als heap-Allokationen in der Realität, es bedeutet sicher nicht stack-Allokationen können immer ersetzen Heapzuweisungen.
Erste, es gibt keinen Weg, um Speicherplatz auf dem Stapel mit einer festgelegten Größe, die zur Laufzeit in einem tragbaren Weise mit ISO-C++. Es gibt Erweiterungen zur Verfügung gestellt von Implementierungen wie
alloca
und G++'s VLA (variable length array), aber es gibt Gründe, um zu vermeiden, verwenden Sie. (IIRC, Linux-Quellcode entfernt Verwendung von VLA vor kurzem.) (Beachten Sie auch die ISO-C99 hat VLA, aber ISO-C11 stellt die Unterstützung optional.)Zweite, es gibt keine zuverlässige und portable Weise zu erkennen stackspeichers. Dies wird Häufig als stack-überlauf (hmm, Etymologie dieser Website), aber wahrscheinlich mehr accruately, "stack overrun". In Wirklichkeit ist dies oft die Ursache Ungültiger Zugriff auf Speicher und den Status des Programms dann corruptied (...oder vielleicht noch schlimmer, eine Sicherheitslücke). In der Tat, ISO-C++ hat kein Konzept von Stapel-und macht es zu undefiniertem Verhalten, wenn die Ressource erschöpft ist. Seien Sie vorsichtig, wie viel Platz gelassen werden sollte für die automatische Objekte.
Wenn der stack-Speicher erschöpft, es gibt zu viele zugeordnete Objekte im stack, die verursacht werden durch zu viele aktive Aufrufe von Funktionen oder unsachgemäße Verwendung von automatischen Objekten. Solchen Fällen möglicherweise vorschlagen, die Existenz von bugs, z.B. eine rekursive Funktion aufrufen, ohne richtige exit-Bedingungen.
Dennoch, tief-rekursive Aufrufe sind manchmal erwünscht. In den Implementierungen der Sprachen, die Unterstützung der ungebundenen aktiven Anrufe (call-Tiefe nur begrenzt durch die Gesamtspeicherkapazität), es ist unmöglich Sie systemeigene Aufrufliste direkt als Zielsprache activation record wie typische C++ - Implementierungen. Zum Beispiel, SML/NJ explizit zuweist frames auf dem heap, und verwendet Kaktus-stacks. Die komplizierte Verteilung solcher activation record-frames ist in der Regel nicht so schnell wie die der call-stack-frames. Allerdings, wenn die Umsetzung weiterer Sprachen mit proper tail recursion, direkte stack Allokation im Objekt-Sprache (also das "Objekt" in der Sprache nicht als Referenz gespeichert, aber die Grundwerte, die kann eins-zu-eins zugeordnet ungeteilt C++ - Objekte) ist sogar noch komplizierter, mit mehr Leistung Strafe im Allgemeinen. Bei der Verwendung von C++ zu implementieren, die solche Sprachen, ist es schwierig einzuschätzen, die Auswirkungen auf die Leistung.
heap
Häufig.heap" kann eindeutig mit einem spezifischen Implementierungen im Auge behalten, also ist es vielleicht OK, manchmal. Es ist überflüssig "im Allgemeinen", obwohl.
Was ist interop?
.Net-spezifische Sachen?
Ich meinte damit alle Arten von "native" - code interoperations beteiligten in die C++ - Quelle, zum Beispiel inline-Assembler-code. Diese stützt sich auf die Annahmen (ABI) nicht unter C++. COM-interop (basierend auf einige Windows-spezifische ABI) ist mehr oder weniger ähnlich, obwohl es meist neutral zu C++.
InformationsquelleAutor FrankHB
Nie tun vorzeitige übernahme als andere Anwendungs-code und-Belastung kann Auswirkungen auf Ihre Funktion. So suchen Funktion, ist die Einsamkeit ist nicht von nutzen.
Wenn dir ernst ist mit der Anwendung dann VTune oder ähnliche profiling-tool und schauen hotspots.
Ketan
InformationsquelleAutor Ketan
Möchte ich sagen, tatsächlich-code generieren, indem Sie GCC (ich erinnere VS auch) nicht haben overhead zu tun stack Allokation.
Sagen für folgende Funktion:
Wird folgender code generiert:
So whatevery, wie viel lokale variable, die Sie haben (auch innerhalb der if-oder switch), nur die 3880 wird sich ändern zu einem anderen Wert. Es sei denn, Sie hat keine lokalen Variablen, die diese Anweisung nur ausführen müssen. So reservieren lokale variable, die nicht Aufwand haben.
InformationsquelleAutor ZijingWu