Ich fange gerade an, CUDA zu verwenden und muss zugeben, dass ich von der C-API ein wenig enttäuscht bin. Ich verstehe die Gründe für die Wahl von C, aber wenn die Sprache stattdessen auf C++ basieren würde, wären einige Aspekte viel einfacher gewesen, z. B. die Zuweisung von Gerätespeicher (über cudaMalloc
).
Mein Plan war es, dies selbst zu tun, indem ich überladene operator new
mit Platzierung new
und RAII (zwei Alternativen). Ich frage mich, ob es irgendwelche Vorbehalte gibt, die ich bisher noch nicht bemerkt habe. Der Code scheint zu arbeiten, aber ich bin immer noch über mögliche Speicherlecks wundern.
Die Verwendung des RAII Der Code würde wie folgt aussehen:
CudaArray<float> device_data(SIZE);
// Use `device_data` as if it were a raw pointer.
Vielleicht ist eine Klasse in diesem Zusammenhang zu viel des Guten (vor allem, weil man immer noch die cudaMemcpy
(die Klasse kapselt nur RAII), so dass der andere Ansatz darin bestehen würde Platzierung new
:
float* device_data = new (cudaDevice) float[SIZE];
// Use `device_data` …
operator delete [](device_data, cudaDevice);
Hier, cudaDevice
dient lediglich als Markierung, um die Überlastung auszulösen. Da jedoch bei normaler Platzierung new
dies würde auf die Platzierung hinweisen, finde ich die Syntax seltsam konsistent und vielleicht sogar besser als die Verwendung einer Klasse.
Ich würde mich über jede Art von Kritik freuen. Weiß vielleicht jemand, ob etwas in dieser Richtung für die nächste Version von CUDA geplant ist (die, wie ich gehört habe, ihre C++-Unterstützung verbessern wird, was auch immer sie damit meinen).
Meine Frage ist also eigentlich eine dreifache:
- Ist meine Platzierung
new
Überlastung semantisch korrekt? Geht dabei Speicherplatz verloren? - Hat jemand Informationen über zukünftige CUDA-Entwicklungen, die in diese allgemeine Richtung gehen (seien wir ehrlich: C-Schnittstellen in C++ sind Mist)?
- Wie kann ich dies auf konsistente Weise weiterführen (es gibt noch andere APIs zu berücksichtigen, z. B. gibt es nicht nur Gerätespeicher, sondern auch einen konstanten Speicher und Texturspeicher)?
// Singleton tag for CUDA device memory placement.
struct CudaDevice {
static CudaDevice const& get() { return instance; }
private:
static CudaDevice const instance;
CudaDevice() { }
CudaDevice(CudaDevice const&);
CudaDevice& operator =(CudaDevice const&);
} const& cudaDevice = CudaDevice::get();
CudaDevice const CudaDevice::instance;
inline void* operator new [](std::size_t nbytes, CudaDevice const&) {
void* ret;
cudaMalloc(&ret, nbytes);
return ret;
}
inline void operator delete [](void* p, CudaDevice const&) throw() {
cudaFree(p);
}
template <typename T>
class CudaArray {
public:
explicit
CudaArray(std::size_t size) : size(size), data(new (cudaDevice) T[size]) { }
operator T* () { return data; }
~CudaArray() {
operator delete [](data, cudaDevice);
}
private:
std::size_t const size;
T* const data;
CudaArray(CudaArray const&);
CudaArray& operator =(CudaArray const&);
};
Über den hier beschäftigten Singleton: Ja, ich bin mir seiner Nachteile bewusst. Allerdings sind diese in diesem Zusammenhang nicht relevant. Alles, was ich hier brauchte, war ein kleines Typ-Tag, das nicht kopierbar war. Alles andere (d.h. Multithreading-Überlegungen, Zeit der Initialisierung) trifft nicht zu.
1 Stimmen
Ihre Implementierung von Singleton ist bestenfalls gefährlich. Bitte lesen Sie die vielen anderen Diskussionen darüber, wie man ein Singleton in C++ erstellt.
0 Stimmen
Ja, du hast Recht. Siehe jedoch meine neue Klarstellung unterhalb des Codes.