Ich habe verschiedene Speicherzuweisungen in meinem Code: Eine für CUDA (verwaltet oder nicht), eine für reinen Host-Speicher. Ich könnte mir auch eine Situation vorstellen, in der Sie verschiedene Zuordnungsalgorithmen verwenden möchten - einen für große, lange lebende Blöcke zum Beispiel und einen anderen für kurzlebige, kleine Objekte.
Ich frage mich, wie man ein solches System richtig implementiert.
Platzierung neu?
Meine aktuelle Lösung verwendet Placement new, wobei der Zeiger entscheidet, welcher Speicher und Speicherzuordner verwendet werden soll. Beim Löschen / Aufheben der Zuordnung der Objekte muss dann vorsichtig vorgegangen werden. Derzeit funktioniert es, aber ich denke, es ist keine nette Lösung.
%Vor%Überladen Sie neue, aber wie?
Ich würde gerne eine Lösung mit einem überladenen new
-Operator wählen. Etwas, das wie folgt aussehen wird:
Ich denke, ich könnte dies erreichen, indem ich einen Namespace CudaAllocator
und HostAllocator
habe, jedes mit einem überladenen new
und delete
.
Zwei Fragen:
new
in einem Code zu haben oder zu haben?
dies ein Zeichen für einen Designfehler? Es gibt eine Zeit und einen Ort, um den Operator new
/ delete
zu überladen, aber es wird im Allgemeinen nur dann bevorzugt, wenn einfachere Maßnahmen ausgeschöpft wurden.
Der Hauptnachteil von placement new
besteht darin, dass der Aufrufer "merkt", wie das Objekt zugewiesen wurde, und die entsprechende Aktion ausführen, um die entsprechende Aufhebung der Zuweisung auszulösen, wenn das Objekt das Ende seiner Lebensdauer erreicht hat. Außerdem muss der Aufrufer das Placement new
aufrufen. Dies ist syntaktisch aufwändig (ich nehme an, dass dies die "nicht nette Lösung" ist, die Sie erwähnen.)
Der Hauptnachteil des Überladens von new
/ delete
ist, dass es für einen bestimmten Typ einmal ausgeführt werden soll (wie @JSF gezeigt hat). Dies koppelt ein Objekt eng an die Art und Weise, wie es zugewiesen / freigegeben wird.
Vorausgesetzt, diese Einrichtung ist:
%Vor% Hier ist MyObj
mit überladenem new
/ delete
(Ihre Frage):
Druckt Folgendes:
MyObj :: neu
MyObj ()
~ MyObj ()
MyObj :: löschen
Eine bessere Lösung könnte die Verwendung von RAII-Zeigertypen in Kombination mit einer Factory sein, um die Details der Zuweisung und Freigabe des Aufrufers zu verbergen. Diese Lösung verwendet das Placement new
, behandelt jedoch die Freigabe, indem eine Deleter-Callback-Methode an eine unique_ptr
angehängt wird.
Drucke:
allocateCudaMemoryField
MyObj ()
allocateHostMemoryField
MyObj ()
~ MyObj ()
deallocateHostMemoryField
~ MyObj ()
deallocateCudaMemoryField
Das wird besser. Mit derselben Strategie können wir die Zuweisungs- / Freigabe-Semantik für jede Klasse handhaben.
%Vor%Wird mit einer neuen Klasse S verwendet:
%Vor%Drucke:
allocateCudaMemoryField
S ()
allocateHostMemoryField
S ()
~ S ()
deallocateHostMemoryField
~ S ()
deallocateCudaMemoryField
Ich wollte den Templating-Full-Blast nicht ankurbeln, aber offensichtlich ist dieser Code reif für DRYing out (parametriere die Implementierungen auf der Allokator-Funktion).
Dies funktioniert sehr gut, wenn Ihre Objekte relativ groß sind und nicht zu oft zugewiesen / freigegeben werden. Ich würde das nicht verwenden, wenn Millionen Objekte in jeder Sekunde kommen und gehen.
Einige der gleichen Strategien funktionieren, aber Sie möchten auch Taktiken wie
betrachtenvector
Es hängt wirklich von Ihren Bedürfnissen ab.
Nein. Überladen Sie nicht new
/ delete
in dieser Situation. Erstellen Sie einen Zuordner, der an Ihre generischen Speicherzuordner delegiert.
Tags und Links memory c++ new-operator cuda