Warum ist die Speicherzuweisung auf dem Heap VIEL langsamer als auf dem Stack?

Lesezeit: 7 Minuten

Benutzeravatar von smwikipedia
smwikipedia

Das wurde mir schon oft gesagt. Aber ich weiß nicht, WARUM 
 Welche zusĂ€tzlichen Kosten entstehen beim Zuweisen von Speicher aus dem Heap? Liegt es an der Hardware? Liegt es an den CPU-Zyklen? So viele Vermutungen, aber keine genauen Antworten 
 Könnte mir jemand etwas nĂ€her erlĂ€utern?

Wie “entspannen” sagte, ist die Heap-Datenstruktur komplizierter als Stack. Und meiner Meinung nach wird einem Thread etwas Speicherplatz als Stack zugewiesen, wenn er zu laufen beginnt, wĂ€hrend der Heap von allen Threads innerhalb eines Prozesses gemeinsam genutzt wird. Dieses Paradigma erfordert einen zusĂ€tzlichen Mechanismus, um die Nutzung des gemeinsam genutzten Heaps durch jeden Thread zu verwalten, wie z. B. Garbage Collection. Liege ich damit richtig?

HINZUFÜGEN 1 – 10:42 AM 13.5.2022

Die Verwaltung eines Stacks betrifft nur die Befehle und Register (SP, BP), was in gewissem Sinne natĂŒrlich/reine Hardware ist.

WĂ€hrend es sich bei einem Heap um komplexe Softwaredatenstrukturen und -algorithmen handelt, die Funktionsaufrufe (wiederum Stack-involviert), Speicherzugriff usw. umfassen.

Hardware ist schnell, aber nicht so flexibel wie Software.

Software ist flexibel, aber nicht so schnell wie Hardware.

Haufen ist also nicht billig.

  • Siehe stackoverflow.com/questions/161053/
, es geht um C++, aber das Konzept ist dasselbe.

    – kennytm

    15. Februar 2010 um 9:40 Uhr

Benutzeravatar von unwind
entspannen

Denn der Heap ist eine weitaus kompliziertere Datenstruktur als der Stack.

Bei vielen Architekturen ist das Zuweisen von Speicher auf dem Stack nur eine Frage des Änderns des Stack-Zeigers, dh es ist eine Anweisung. Das Zuweisen von Speicher auf dem Heap beinhaltet das Suchen nach einem ausreichend großen Block, dessen Aufteilung und die Verwaltung der “Buchhaltung”, die Dinge wie ermöglicht free() in einer anderen Reihenfolge.

Auf dem Stapel zugewiesener Speicher wird garantiert freigegeben, wenn der GĂŒltigkeitsbereich (normalerweise die Funktion) beendet wird, und es ist nicht möglich, nur einen Teil davon freizugeben.

  • Der letzte Satz ist etwas verwirrend. Anstatt zu sagen “ist auf einmal verloren” wĂŒrde ich sagen, dass es garantiert in der umgekehrten Reihenfolge freigegeben wird, in der es zugewiesen wurde.

    – Laurence Gonsalves

    15. Februar 2010 um 9:53 Uhr

Benutzeravatar von b4hand
b4hand

In Ihrer Bearbeitung, in der Sie die Antwort von Unwind wiederholen, erwĂ€hnen Sie die “Heap-Datenstruktur”. Seien Sie sehr vorsichtig, da die als a Haufen hat keine Beziehung zur dynamischen Speicherzuweisung. Um ganz klar zu sein, werde ich die mehrsprachige Anwaltsterminologie von verwenden kostenlos speichern.

Wie bereits erwĂ€hnt wurde, erfordert die Stack-Zuweisung das Inkrementieren eines Zeigers, der in den meisten Architekturen typischerweise ein dediziertes Register hat, und das Aufheben der Zuweisung erfordert den gleichen Arbeitsaufwand. Stack-Zuweisungen sind auch auf eine bestimmte Funktion beschrĂ€nkt. Dies macht sie zu viel besseren Kandidaten fĂŒr Compiler-Optimierungen wie das Vorberechnen des gesamten auf dem Stack benötigten Speicherplatzes und das AusfĂŒhren eines einzelnen Inkrements fĂŒr einen gesamten Stack-Frame. Ebenso hat der Stack eine besser garantierte DatenlokalitĂ€t. Die Spitze des Stapels befindet sich fast immer garantiert innerhalb einer Cache-Zeile, und wie ich bereits erwĂ€hnt habe, wird der Stapelzeiger normalerweise in einem Register gespeichert. Die Optimierung von Compilern auf einigen Architekturen kann sogar Zuweisungen auf dem Stack vollstĂ€ndig eliminieren, indem Argumente aus vorherigen Stack-Frames wiederverwendet werden, die als Argumente an aufgerufene Funktionen in tieferen Stack-Frames ĂŒbergeben werden. Ebenso können Stack-zugewiesene Variablen oft in Register hochgestuft werden, um Zuweisungen ebenfalls zu vermeiden.

Im Gegensatz dazu ist der kostenlose Shop viel komplexer. Ich werde nicht einmal anfangen, Garbage-Collection-Systeme zu behandeln, da dies ein ganz anderes Thema ist, und diese Frage wurde in Bezug auf die C-Sprache gestellt. Typischerweise beinhalten Zuweisungen und Freigaben von einem freien Speicher mehrere unterschiedliche Datenstrukturen wie eine freie Liste oder einen Blockpool. Diese Datenstrukturen und die BuchfĂŒhrung erfordern ebenfalls Speicherplatz, und somit wird dieser Speicherplatz verschwendet. Außerdem werden die Buchhaltungsunterlagen oft mit den Zuweisungen vermischt und beeintrĂ€chtigen somit die DatenlokalitĂ€t anderer Zuweisungen. Zuweisungen aus dem freien Speicher können beinhalten, dass das zugrunde liegende Betriebssystem nach mehr Prozessspeicher gefragt wird, typischerweise von irgendeiner Art von Slab-Zuordner.

FĂŒr einen einfachen Vergleich und unter Verwendung von jemalloc-2.2.5 und Zahlen von sloccount als Referenz enthĂ€lt die jemalloc-Implementierung ĂŒber 8.800 Zeilen Quellcode in der Sprache C und weitere ĂŒber 700 Zeilen Testcode. Dies sollte Ihnen eine gute Vorstellung vom KomplexitĂ€tsunterschied zwischen freier Speicherzuweisung und Stapelzuweisung geben: Tausende von Zeilen C-Code im Vergleich zu einer einzelnen Anweisung.

Da außerdem freie Speicherzuordnungen nicht auf einen einzigen lexikalischen Bereich beschrĂ€nkt sind, muss die Lebensdauer jeder Zuordnung verfolgt werden. Ebenso können diese Zuweisungen ĂŒber Threads hinweg weitergegeben werden, und daher treten Thread-Synchronisationsprobleme in den Problemraum ein. Ein weiteres großes Problem fĂŒr die kostenlose Speicherzuweisung ist die Fragmentierung. Fragmentierung verursacht viele Probleme:

  • Fragmentierung schadet der DatenlokalitĂ€t.
  • Fragmentierung verschwendet Speicher.
  • Fragmentierung erschwert die Suche nach freiem Speicherplatz fĂŒr große Zuweisungen.

Auf modernen Systemen sind Stacks im Vergleich zum kostenlosen Speicher oft relativ klein, sodass der kostenlose Speicher letztendlich mehr Speicherplatz verwaltet und somit ein schwierigeres Problem angeht. Aufgrund der BeschrĂ€nkungen der Stack-GrĂ¶ĂŸen wird der kostenlose Speicher typischerweise auch fĂŒr grĂ¶ĂŸere Zuordnungen verwendet, diese Diskrepanz zwischen der Handhabung sowohl sehr großer als auch sehr kleiner Zuordnungen erschwert die Arbeit des kostenlosen Speichers ebenfalls. Typischerweise sind Stapelzuordnungen klein in der GrĂ¶ĂŸenordnung von einigen Kilobyte oder weniger, und die GesamtgrĂ¶ĂŸe des Stapels betrĂ€gt nur wenige Megabyte. Der kostenlose Shop ist in der Regel die gegeben gesamten restlichen Prozessraum in einem Programm. Auf modernen Computern können dies mehrere hundert Gigabyte sein, und es ist nicht ungewöhnlich, dass kostenlose Speicherzuordnungen in der GrĂ¶ĂŸe von wenigen Bytes wie einer kurzen Zeichenfolge bis zu Megabytes oder sogar Gigabytes beliebiger Daten variieren. Dies bedeutet, dass Zuordner fĂŒr freie Speicher sich mit der Verwaltung des virtuellen Speichers des zugrunde liegenden Betriebssystems befassen mĂŒssen. Die Stapelzuweisung ist im Wesentlichen in die Computerhardware integriert.

Wenn Sie wirklich etwas ĂŒber die kostenlose Speicherzuweisung erfahren möchten, empfehle ich Ihnen dringend, einige der vielen Artikel und Artikel zu lesen, die ĂŒber verschiedene Malloc-Implementierungen veröffentlicht wurden, oder sogar den Code zu lesen. Hier sind ein paar Links fĂŒr den Einstieg:

  • dlmalloc – Malloc von Doug Lea, eine historische Malloc-Referenzimplementierung, die zu einem bestimmten Zeitpunkt in GNU C++ verwendet wurde
  • Phkmalloc – FreeBSD-Implementierung von malloc, geschrieben von Poul-Henning Kamp, dem Autor des Varnish-Webcaches
  • tcmalloc – Thread-Caching Malloc, implementiert von einigen Google-Entwicklern
  • jemalloc – Jason Evans malloc-Implementierung fĂŒr FreeBSD (Nachfolger von phkmalloc)

Hier sind einige zusÀtzliche Links mit Beschreibungen der tcmalloc-Implementierung:

Der Hauptunterschied zwischen einem Stapel und einem Haufen besteht darin, dass GegenstĂ€nde auf einem Stapel nicht aus der Reihenfolge entfernt werden können. Wenn Sie die Elemente A, B, C zu einem Stapel hinzufĂŒgen, können Sie B nicht entfernen, ohne zuerst C zu entfernen. Das bedeutet, dass das HinzufĂŒgen eines neuen Elements zu einem Stapel immer ein HinzufĂŒgen zum Stapel bedeutet Ende des Stapels, was eine sehr einfache Operation ist. Sie bewegen einfach den Zeiger, der auf das Ende des Stapels zeigt.

Auf einem Haufen hingegen du kann Artikel aus der Reihenfolge entfernen. Und solange Sie die anderen Elemente nicht nachtrĂ€glich im Speicher verschieben (wie es einige MĂŒllhaufen tun), hat Ihr Haufen dann ein “Loch” in der Mitte. Dh wenn Sie A, B, C zu einem Heap hinzufĂŒgen und B entfernen, sieht Ihr Heap im Speicher so aus: A _ C wobei _ ein Block unbenutzten (freien) Speichers ist. Wenn Sie jetzt ein neues Element D hinzufĂŒgen, muss der Zuordner einen zusammenhĂ€ngenden freien Speicherplatz finden, der groß genug ist, um D aufzunehmen. Je nachdem, wie viele zusammenhĂ€ngende freie PlĂ€tze in Ihrem Speicher vorhanden sind, kann dies eine teure Operation sein. Und es ist fast immer teurer, als nur den “letztes Element”-Zeiger eines Stapels zu verschieben.

Erstellung von Daten im Stapelbereich: Bewegen Sie einfach den Stapelzeiger. Erstellung von Daten im Kopfbereich: Suchen Sie nach einem Bereich im Speicherpool, der die angegebene Anforderung erfĂŒllt (Sie können First Fit, Best Fit oder Worst Fit verwenden). Nachdem Sie den Bereich gefunden haben, speichern Sie die Informationen (BuchfĂŒhrung)

Löschen auf Stapel : Löschen auf Stapel ist einfach. Bewegen Sie einfach den Stapelzeiger nach unten. Löschen auf Haufenbereich : Finden Sie heraus, wo das Element auf dem Haufen gespeichert ist (unter Verwendung der Buchhaltung) und fĂŒhren Sie zwei benachbarte freie Blöcke zusammen, falls erforderlich;

1416270cookie-checkWarum ist die Speicherzuweisung auf dem Heap VIEL langsamer als auf dem Stack?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy