Konzeptfrage: dynamischer Speicher
-
@groovemaster: Bitte führe doch mal genau aus, wo du bei den vorhandenen Verfahren konkret Nachteile siehst, unabhängig von Syntax. Also ein bisschen über konkrete Programmiersprachen hinaus abstrahiert.
Ich verstehe deine Kritikpunkte nämlich nicht völlig. Heutzutage unterscheidet man bei der Resourcenfreigabe oft zwei Arten:
- Resourcen, die sofort freigegeben werden müssen, wenn sie nicht mehr gebraucht werden. Dazu gehören Socketverbindungen, File-Handles, usw. Hier sehe ich Handlungsbedarf. Meiner Meinung nach decken hier weder C++-style Destruktoren noch try-finally Aktionen den Bedarf wirklich ab, da sie ausschließlich Scope-basiert arbeiten.
- Resourcen, die nur freigegeben werden müssen, wenn Bedarf steht, meistens Speicher. Hier verstehe ich nicht, was du gegen GC hast. GC ist meistens effizienter als deterministische Destruktion, weil viel Speicher auf einmal freigegeben werden kann, dagegen wenn du ein komplexes Objekt oft erzeugst und destruierst, dann wird davon der Destruktor aufgerufen der sowas "nutzloses" macht wie den freien Speicher wieder in die Freispeicherliste einzuhängen. Von davon referenzierten Objekten wird der Destruktor aufgerufen, usw. kann ewig aufwändig sein, vor allem völlig nutzlos wenn du gerade gar keinen Bedarf an Heap-Speicher hast.
Deine Frage zielte jetzt anscheinend nur auf Speicherverwaltung ab, also frage ich mich: wo siehst du bei heutigen modernen GCs dringenden Handlungsbedarf? In real-world Applikationen macht der GC meistens weniger als 1% der Laufzeit aus, während Sachen wie Referenzcounnting, scope-basierte Speicherfreigabe oft viel teurer sind und auch nicht immer hinreichend. GC ist also sicherer, effizienter und einfacher als andere Verfahren die es heute gibt (im allgemeinen Fall).
Trotzdem würde mich dein Vorschlag natürlich auch interessieren. Die Begründung "faul" ist übrigens eher etwas, was für GC spricht.

-
Simon2 schrieb:
vorab: Mir ist hier noch nicht ganz klar, welches Problem Du mit Deinem neuen Konzept addressieren möchtest.
Kein konkretes. Mir geht es lediglich darum, wie man grundlegend ein dynamisches Speichermanagement konzipieren könnte, ohne auf bisherige Konzepte Rücksicht nehmen zu müssen, aber trotzdem alle Vorteile zu vereinen ohne die entsprechenden Nachteile.
Simon2 schrieb:
Zum Konzept: Wann soll wer dem Compiler mitteilen, dass der Speicher freigegeben werden kann ... oder soll das per reference counting doch automatisiert geschehen ?
Soll man statt eines deletes dann den Scope seiner "P^-Variablen" verlassen (was einem aber nur bedingt weiterhilft ... der Aufgerufene hat evtl. den Pointer weitergereicht) ?Und wo ist dann der Vorteil ggü. einem GC ?
Darum geht es ja eben. Die Antworten darauf kann ich dir nicht geben, deshalb wollte ich wissen, was ihr für ein gutes Konzept haltet. Oder seit ihr mit den bisherigen wunschlos glücklich?
daHa schrieb:
ein paradebsp zum thema wie beginn ich eine sachlich vernuenftige diskussion?
Nein, da hast du was falsch verstanden. Das soll keine Diskussion werden, sondern vielmehr eine Ideensammlung. Wer dieses oder jenes Konzept anpreisen oder darüber ablästern will, zB GC, soll das in einem anderen Thread machen.
Artchi schrieb:
Also ich verstehe dein Problem nicht. Worauf willst du hinaus? Willst du darüber diskuttieren, was man an C++' dynamischer Speicherverwaltung bessern könnte?
1. Rein vom Backend? Also was passiert zur Laufzeut?
2. Oder willst du die Handhabung für den Programmierer z.B. über neue Features verbessern?Nein, ja und ja. Mir geht es zwar nicht konkret um C++, aber man könnte zB eine "fiktive" C++ Sprache erstmal als Orientierung nehmen. Wie würde das dynamische Speicherhandling in "Fiktiv C++" aussehen, wenn es keine Zeiger gäbe? Ich hoffe, du verstehst worauf ich hinaus will. Und dabei geht es mir um Kernfunktionalität der Sprache, und nicht um irgendwelche zusätzlichen Klassen, Bibliotheken oder konkrete Implementationen bzw. den darin benutzten Strategien. Also grob gesagt, wie kann ich in meinem Quellcode den Compiler veranlassen, ein dynamisches Objekt anzulegen bzw. zu zerstören? Und was passiert dabei bzw. mit diesen Objekten hinter den Kulissen?
OK, um es vllt. nochmal etwas zu verdeutlichen. Momentan stören mich beim C++ Speichermanagement folgendes:
- eingeschränkte Funktionalität und aufwändige Syntax beim Erzeugen/Zerstören über new/delete - ähh, wie kann man nochmal Arrays bei der Erzeugung initialisieren? - wieso muss ich delete [] mitgeben, wenn ich ein Array habe? (die Antwort ist mir schon klar, da anhand eines Zeigers nicht festgestellt werden kann, ob Array oder nicht; aber da kommen wir zum nächsten Punkt) - wieso werden Zeiger verwendet? ich möchte zB dynamische Objekte genauso einfach und unkompliziert wie automatische verwenden können - Sicherheitsmängel - delete a; delete a; zu schreiben ist kein Problem, die Anwendung ist damit jedoch kaputt - vergessene deletes verursachen Speicherleichen - ein "unbeabsichtigt" überschriebener Zeiger, der vorher das Ergebnis von new enthielt, hinterlässt ebenfalls Speicherleichen - nicht exception-safeMehr fällt mir jetzt erstmal nicht, gibt aber bestimmt noch weitere Sachen.
Zum Thema GC. Den schliesse ich erstmal aus folgenden Gründen aus:
- aufgrund der Unberechenbarkeit von Freigaben nur bedingt geeignet für Real-Time Systeme - unzureichende Effizienz zB wird innerhalb eines Scopes ein dynamisches Objekt angelegt sobald der Scope verlassen wird, soll auch dieses Objekt sterben und damit der Speicher freigegeben werden ein GC lässt sich hier jedoch unbestimmt viel Zeit mit der FreigabeOptimizer schrieb:
Trotzdem würde mich dein Vorschlag natürlich auch interessieren. Die Begründung "faul" ist übrigens eher etwas, was für GC spricht.

Ja, das war ja auch nicht negativ gemeint. Programmierer sind grundsätzlich faul, und da ist es gut, wenn man soviel wie möglich Arbeit abgenommen bekommt. Nur ist das momentan der einzige Punkt, den ich bei einem GC positiv bewerten würde. Das soll jetzt aber kein Diskussionsthread zum Thema GC werden. Also zurück zum eigentlichen Thema.
-
groovemaster schrieb:
- unzureichende Effizienz
zB wird innerhalb eines Scopes ein dynamisches Objekt angelegt
sobald der Scope verlassen wird, soll auch dieses Objekt sterben und damit der Speicher freigegeben werden
ein GC lässt sich hier jedoch unbestimmt viel Zeit mit der FreigabeDas erhöht die Effizienz. Wir reden hier ja von Speicherverwaltung, nicht von Socket-Verbindung-Verwaltung. Jedes kleine Fitzel 16B explizit in die Freispeicherliste hundertmal einzutragen anstatt 1mal 1,6 KB freizuräumen ist langsamer. Das musst du dir wirklich bewusst machen, wenn du über Speicherverwaltung nachdenkst.
Also zurück zum eigentlichen Thema.
Gerne. Kannst du das Thema bitte noch genauer eingrenzen, Speicherverwaltung gehört dazu, aber GC nicht?
Ich will mich echt konstruktiv beteiligen, aber mir ist das manches nicht klar. Beschränkst du dich auf nicht-automatische Speicherverwaltung?
-
Optimizer schrieb:
Deine Frage zielte jetzt anscheinend nur auf Speicherverwaltung ab, also frage ich mich: wo siehst du bei heutigen modernen GCs dringenden Handlungsbedarf? In real-world Applikationen macht der GC meistens weniger als 1% der Laufzeit aus, während Sachen wie Referenzcounnting, scope-basierte Speicherfreigabe oft viel teurer sind und auch nicht immer hinreichend. GC ist also sicherer, effizienter und einfacher als andere Verfahren die es heute gibt (im allgemeinen Fall).
GCs eignen sich aber nicht dazu kritische Ressourcen freizugeben, da destruieren eben nicht garantiert ist.
-
Ja, diese Unterscheidung habe ich weiter oben vorgenommen. Full ack.
-
Optimizer schrieb:
groovemaster schrieb:
- unzureichende Effizienz
zB wird innerhalb eines Scopes ein dynamisches Objekt angelegt
sobald der Scope verlassen wird, soll auch dieses Objekt sterben und damit der Speicher freigegeben werden
ein GC lässt sich hier jedoch unbestimmt viel Zeit mit der FreigabeDas erhöht die Effizienz. Wir reden hier ja von Speicherverwaltung, nicht von Socket-Verbindung-Verwaltung. Jedes kleine Fitzel 16B explizit in die Freispeicherliste hundertmal einzutragen anstatt 1mal 1,6 KB freizuräumen ist langsamer. Das musst du dir wirklich bewusst machen, wenn du über Speicherverwaltung nachdenkst.
Darüber hinaus kommt hinzu, dass moderne GC-Verfahren grade bei kurzlebigen Objekten (d.h. die nur in einem engen Scope existieren) besonders effektiv sind - Stichwort Generation Scavenging. Heutzutage gibt es wirklich keinen Grund mehr, vor Garbage-Collection angst zu haben. Höchstens man ist auf Echtzeitfähigkeit angewiesen.
tfa
-
tfa schrieb:
Optimizer schrieb:
groovemaster schrieb:
- unzureichende Effizienz
zB wird innerhalb eines Scopes ein dynamisches Objekt angelegt
sobald der Scope verlassen wird, soll auch dieses Objekt sterben und damit der Speicher freigegeben werden
ein GC lässt sich hier jedoch unbestimmt viel Zeit mit der FreigabeDas erhöht die Effizienz. Wir reden hier ja von Speicherverwaltung, nicht von Socket-Verbindung-Verwaltung. Jedes kleine Fitzel 16B explizit in die Freispeicherliste hundertmal einzutragen anstatt 1mal 1,6 KB freizuräumen ist langsamer. Das musst du dir wirklich bewusst machen, wenn du über Speicherverwaltung nachdenkst.
Darüber hinaus kommt hinzu, dass moderne GC-Verfahren grade bei kurzlebigen Objekten (d.h. die nur in einem engen Scope existieren) besonders effektiv sind - Stichwort Generation Scavenging. Heutzutage gibt es wirklich keinen Grund mehr, vor Garbage-Collection angst zu haben. Höchstens man ist auf Echtzeitfähigkeit angewiesen.
tfa
wobei mir einraetsel ist warum zeugs das sowieso nur in einem bekannten scope exestiert ueberhpt in den gc kommen soll/muss.
wie kanns performatner sein wenn ich eine komponete hab die ueber was nachdenken muss anstatt ohne nachdenken das richtige zu machen?
und das 100x16b vs 1x1,6kb freigeben, woher weis der gc das es durchgaengige speicherbereiche sind die er einfach so als block freigeben kann?
wurde da schon zeit vorverbraten um zu analysieren und organisieren?
und wird diese zeit in der effizienzberechnung nicht beruecksichtigt?
-
tfa schrieb:
Darüber hinaus kommt hinzu, dass moderne GC-Verfahren grade bei kurzlebigen Objekten (d.h. die nur in einem engen Scope existieren) besonders effektiv sind - Stichwort Generation Scavenging. Heutzutage gibt es wirklich keinen Grund mehr, vor Garbage-Collection angst zu haben.
nur wenn man es selbst schon richtig managed, dann ist ein GC nur ueberfluessig.
-
rapso schrieb:
tfa schrieb:
Darüber hinaus kommt hinzu, dass moderne GC-Verfahren grade bei kurzlebigen Objekten (d.h. die nur in einem engen Scope existieren) besonders effektiv sind - Stichwort Generation Scavenging. Heutzutage gibt es wirklich keinen Grund mehr, vor Garbage-Collection angst zu haben.
nur wenn man es selbst schon richtig managed, dann ist ein GC nur ueberfluessig.
Managt man es denn richtig? Es ist doch grade der Vorteil, dass man mit GC sehr viel weniger selber managen muss. Die eingesparte Zeit kann man prima nutzen, um Software zu entwicklen, statt sich tagelangen Debug-Sessions hinzugeben um Speicherlecks zu finden.
Die Performance-Einbußen durch GC sind meiner Meinung nach auch zu vernachlässigen. Die Steigerung der Entwickler-Performance allerdings ganz und gar nicht.tfa
-
daHa schrieb:
wobei mir einraetsel ist warum zeugs das sowieso nur in einem bekannten scope exestiert ueberhpt in den gc kommen soll/muss.
Soll/muss keineswegs so sein. Was ich sinnvoll auf den Stack packen kann, sollte ich da auch hintun, GC sollte IMHO kein Ersatz für Stack-Allokationen sein. Es soll ein Ersatz für new ... delete sein, für Auto-Pointer und so stuff.
und das 100x16b vs 1x1,6kb freigeben, woher weis der gc das es durchgaengige speicherbereiche sind die er einfach so als block freigeben kann?
wurde da schon zeit vorverbraten um zu analysieren und organisieren?
und wird diese zeit in der effizienzberechnung nicht beruecksichtigt?Moderne GCs deallokieren Objekte nicht explizit und fassen dann freie Speicherblöcke zusammen. Stattdessen verschieben sie die Objekte die noch gebraucht werden in einen anderen Bereich, der Rest ist dann per Definition wieder freier Speicher. Allokieren geht dann auch deutlich schneller, weil nicht ein freier Speicherblock gesucht wird, denn der freie Speicher ist imer defragmentiert. Man erhöht genauso wie beim Stack einfach den "free space" Pointer.
Du kannst dir mal diesen Artikel ansehen: http://msdn.microsoft.com/msdnmag/issues/1100/GCI/
Da ist ein bisschen Wirtschaftler-Geblubber drin wie toll GC ist, aber man gewinnt einen guten Überblick darüber, wie sie funktioniert.
-
tfa schrieb:
rapso schrieb:
tfa schrieb:
Darüber hinaus kommt hinzu, dass moderne GC-Verfahren grade bei kurzlebigen Objekten (d.h. die nur in einem engen Scope existieren) besonders effektiv sind - Stichwort Generation Scavenging. Heutzutage gibt es wirklich keinen Grund mehr, vor Garbage-Collection angst zu haben.
nur wenn man es selbst schon richtig managed, dann ist ein GC nur ueberfluessig.
Managt man es denn richtig? Es ist doch grade der Vorteil, dass man mit GC sehr viel weniger selber managen muss. Die eingesparte Zeit kann man prima nutzen, um Software zu entwicklen, statt sich tagelangen Debug-Sessions hinzugeben um Speicherlecks zu finden.
Die Performance-Einbußen durch GC sind meiner Meinung nach auch zu vernachlässigen. Die Steigerung der Entwickler-Performance allerdings ganz und gar nicht.tfa
rapso meint sowas, worauf du nicht eingehst:
void foo() { std::string str; }Wo muß ich da mehr managen und nachdenken, als wenn ich einen GC hätte? Genau das mache ich, wenn ich ein Objekte anlege, das nur in einem Scope Gültigkeit haben soll. In DEM Fall, ist ein GC in keiner Weise hilfreicher. Und der GC räumt mein str-Objekt auch nicht schneller auf, als wenn der Compiler nur den Stackpointer hoch oder runter zählt.
Klar, wenn ich mit new etwas anlege, halt dynamisch (worum es hier in dem Thread auch geht!!!), kann mir ein GC Zeit beim Entwickeln einsparen. Richtig. Aber das werden wir in C++ auch ab 2009 laut neuen Standard machen können. Da es in diesem Thread aber laut groovemaster nicht speziell um C++ geht, ist das eigentlich auch nebensächlich.
-
Wie fordert der string gleich nochmal intern seinen Speicher an? Oha, dynamisch...
-
Ja und? Was hab ich als Programmierer damit zutun? Ich habe schliesslich auf tfa's Beitrag geantwortet. Dem es rein um den Aufwand für den Programmierer ging: "ohne GC viel mehr Tipparbeit und mehr nachdenken, wenn ich Scopeobjekte habe"... was aber falsch ist. Egal was der String intern macht.
Und falls es um Performance geht: wie kann ein GC generell schneller sein als ein new/delete? Notfalls nimm ich SmartHeap dazu. was aber auch beweist, das ein Heap-Management nur "schlau" implementiert sein muß. Völlig unabhängig ob ich einen GC nehme oder eine Library wie SmartHeap. Ein GC ist schliesslich keine Zauberei, oder doch? Und weiterhin: auch ei GC kann schlecht implemntiert und designed sein. Wer garantiert mir, wenn ich einen GC nehme, das ich auch Perfomancevorteile bekomme? Rein Zufällig mögen die GC von MS gut sein, aber die SUN Java-GC ist z.B. schlechter als der von anderen Java-Implementierern. Auch wenn ich das Weltbild einiger SUN-Fans damit zerstöre...
Artchi
PS: Mist, ich habe jetzt Java mit ins Spiel gebracht. Das wird mir jetzt zum Verhängnis!

-
Ein GC ist schon nett, da man beim selbst Managen schnell Fehler einhandeln kann. Wobei man zu den Geschwindigkeitsvorteilen die Optimizer aufgezählt hat aber sagen muss, dass dies oft nur die Frage des Allokators/Deallokators ist. Man kann ja auch einen Allokator/Deallokator schreiben, der wie ein moderner GC funktioniert. Der Deallokator sagt einfach nur "der Speicher hier ist zwar noch reserviert aber eigentlich frei" und der Allokator durchsucht diese Liste bevor er neuen Speicher anlegt (oder so ähnlich).
groovemaster schrieb:
Ihr seht schon, es ist also eine Art in die Sprache integrierter auto_ptr.
wozu sollte man auto_ptr in die Sprache integrieren, wenn man es genauso gut als Template-Klasse schreiben kann, wie es bisher ist?
-
wozu sollte man auto_ptr in die Sprache integrieren, wenn man es genauso gut als Template-Klasse schreiben kann, wie es bisher ist?
Das Argument vieler ist halt, das alles Buildin sein muß. Egal ob es Sinn macht oder nicht. Genau das werden wir aber in Zukunft in C++ weniger sehen, laut C++ Komitee. Mehr über Libraries und Templates, in die C++-Core nur noch das nötigste.
-
Artchi schrieb:
auch ei GC kann schlecht implemntiert und designed sein. Wer garantiert mir, wenn ich einen GC nehme, das ich auch Perfomancevorteile bekomme? Rein Zufällig mögen die GC von MS gut sein, aber die SUN Java-GC ist z.B. schlechter als der von anderen Java-Implementierern. Auch wenn ich das Weltbild einiger SUN-Fans damit zerstöre...
Einen schlechten GC kann man ja leicht ersetzen (also leicht im Sinne von ich nehme einen besseren GC und packe den dazu und entferne den schlechten, nicht leicht im Sinne von ich schreib mir mal eben einen guten GC ;)). Die manuelle Verwaltung zu ändern dürfte deutlich schwieriger sein
-
Ja, worum geht es denn nun? Um die Manuelle Verwaltung vs. Automatische Verwaltung? Oder um Performance?

Wenn es um Performance geht (GC soll schneller sein), sage ich, dann kann ich einfach die Standardlibrary durch eine bessere tauschen (z.B. die SmartHeap nutzt). Hab ich also auch einen Austausch vorgenommen. Muß ich zwar neu kompilieren, interessiert aber den Endanwender nicht, welche Lib ich nutze. Hauptsache das Ding rennt.
Wenn es um den Programmierer geht: spielt das "Austauschen können" des GCs keine Rolle. Entweder ich nimm einen GC oder nicht. Beides kann ich heute machen. Ich kann es manuell machen, ich kann Smartpointer nehmen, oder ich nehme einen GC. Und ab 2009 haben wir eh offiziell einen GC im ISO-C++. Die Diskussion werden wir ab dann hoffentlich hier nicht mehr haben.
-
rüdiger schrieb:
Wobei man zu den Geschwindigkeitsvorteilen die Optimizer aufgezählt hat aber sagen muss, dass dies oft nur die Frage des Allokators/Deallokators ist. Man kann ja auch einen Allokator/Deallokator schreiben, der wie ein moderner GC funktioniert. Der Deallokator sagt einfach nur "der Speicher hier ist zwar noch reserviert aber eigentlich frei" und der Allokator durchsucht diese Liste bevor er neuen Speicher anlegt (oder so ähnlich).
Hmmm. Ein Grundproblem bleibt aber auch mit eigenen Allokatoren bestehen: Du machst jede Freigabe explizit. Der Allokator kann das natürlich ansammeln und dann größere Bereiche auf einmal freigeben, aber *irgendetwas* machst du immer beim Deallokieren. Ein gescheiter GC dagegen deallokiert nicht.
Artchi schrieb:
Und falls es um Performance geht: wie kann ein GC generell schneller sein als ein new/delete?
IEs ist in der Praxis wirklich oft so (ich sag jetzt mal absichtlich nicht "meistens"). Das hat viele Gründe, new muss freien Speicher suchen, delete muss ausgeführt werden (ein guter GC macht kein delete), Speicher kann fragmentieren und schlechte Lokalität haben, es gibt hunderte Gründe, warum ein GC schneller oder langsamer ist. Du wirst natürlich trotzdem immer was konstruieren können, wo eine klassische Heap-Verwaltung geiler ist. In der Praxis ist er aber oft schneller. Ein eigener Allokator kann wieder noch besser sein, aber darauf hast du doch auch eher selten Lust.
aber die SUN Java-GC ist z.B. schlechter als der von anderen Java-Implementierern
Der ist wirklich grottig, hat Pause-Times im Zehntelsekunden-Bereich, da schaudert's mich echt. Die, die am meisten rumlabern, bauen echt den schlechtesten.

-
Artchi schrieb:
Und ab 2009 haben wir eh offiziell einen GC im ISO-C++. Die Diskussion werden wir ab dann hoffentlich hier nicht mehr haben.
Was glaubst du, wie die dann erst losgeht.
"soll ich ihn verwenden oder nicht?"
-
GCs sind doch im Prinzip schneller, weil sie Arbeit verschieben können und weil sie leicht den Heap aufräumen können. Aber das ist auch mit anderen Allokatoren möglich.
@Optimizer
dieses irgend was ist aber vermutlich nicht der Rede wert. Wenn doch, dann kann man bei der manuellen Verwaltung die Freigabe ja einfach auf einen günstigeren Zeitpunkt verlegen. (So wie man den meisten GCs ja auch sagen kann "Nerv getz nich!" ;))Artchi schrieb:
Ja, worum geht es denn nun? Um die Manuelle Verwaltung vs. Automatische Verwaltung? Oder um Performance?

Wenn es um Performance geht (GC soll schneller sein), sage ich, dann kann ich einfach die Standardlibrary durch eine bessere tauschen (z.B. die SmartHeap nutzt). Hab ich also auch einen Austausch vorgenommen. Muß ich zwar neu kompilieren, interessiert aber den Endanwender nicht, welche Lib ich nutze. Hauptsache das Ding rennt.
Bei manueller Verwaltung kannst du natürlich die Allokatoren austauschen, aber du kannst nicht einfach die interne Verwaltung umstricken. Das wollte ich eigentlich sagen. Man kann ja teilweise die GCs sogar vor dem starten einer Anwendung wählen.