Zaczynam używać CUDA w tej chwili i muszę przyznać, że jestem trochę rozczarowany C API. Rozumiem powody wyboru języka C, ale jeśli język był oparty na C++, kilka aspektów byłoby o wiele prostszych, np. przydzielanie pamięci urządzenia (via cudaMalloc
).CUDA: Alokacja pamięci urządzenia pakującego w C++
Mój plan polegał na zrobieniu tego samemu, używając przeciążonego operator new
z miejscami docelowymi new
i RAII (dwie alternatywy). Zastanawiam się, czy są jakieś zastrzeżenia, których do tej pory nie zauważyłem. Kod wydaje się być do pracy, ale wciąż zastanawiam się nad potencjalnymi wyciekami pamięci.
Korzystanie z RAII kodu byłby następujący:
CudaArray<float> device_data(SIZE);
// Use `device_data` as if it were a raw pointer.
Może klasa jest przesadą w tym kontekście (zwłaszcza odkąd trzeba jeszcze używać cudaMemcpy
klasa tylko enkapsulacji RAII) więc inne podejście byłoby placement new
:
float* device_data = new (cudaDevice) float[SIZE];
// Use `device_data` …
operator delete [](device_data, cudaDevice);
Tutaj cudaDevice
działa jedynie jako znak do wywołać przeciążenie. Ponieważ jednak w zwykłym umieszczeniu new
wskazuje to na umieszczenie, uważam, że składnia jest dziwnie spójna i być może nawet bardziej pożądana niż użycie klasy.
Byłbym wdzięczny za krytykę każdego rodzaju. Czy ktoś może wie, czy coś w tym kierunku jest planowane dla następnej wersji CUDA (co, jak słyszałem, poprawi jej obsługę C++, cokolwiek mają na myśli).
Więc moje pytanie jest faktycznie trojakie:
- Czy mój placement
new
przeciążenie semantycznie poprawne? Czy przecieka pamięć? - Czy ktoś ma informacje o przyszłych projektach CUDA, które mają ten ogólny kierunek (spójrzmy prawdzie w oczy: interfejsy C w C++ s * ck)?
- Jak mogę dalej to robić w spójny sposób (istnieją inne API do rozważenia, np. Nie tylko pamięć urządzenia, ale także stały magazyn pamięci i pamięć tekstur)?
// 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&);
};
O Singleton zatrudniony tutaj: Tak, jestem świadoma swoich wad. Jednak nie są one istotne w tym kontekście. Jedyne, czego potrzebowałem, to mały tag, którego nie można było skopiować. Cała reszta (tj. Wielowątkowość, czas inicjalizacji) nie ma zastosowania.
Twoja implementacja singletonu jest w najlepszym wypadku niebezpieczna. Zobacz wiele innych dyskusji na temat tworzenia singleton w C++. –
Tak, masz rację. Zobacz jednak moje nowe wyjaśnienie poniżej kodu. –