Я начинаю использовать CUDA на данный момент и должен признать, что я немного разочарован C API. Я понимаю причины выбора C, но вместо того, чтобы язык был основан на С++, несколько аспектов были бы намного проще, например. (через cudaMalloc
).
Мой план состоял в том, чтобы сделать это сам, используя перегруженный operator new
с размещением new
и RAII (две альтернативы). Мне интересно, есть ли какие-либо предостережения, которых я пока не заметил. Код, похоже, работает, но мне все еще интересно узнать о потенциальных утечках памяти.
Использование кода RAII будет следующим:
CudaArray<float> device_data(SIZE);
// Use `device_data` as if it were a raw pointer.
Возможно, класс переполнен в этом контексте (тем более, что вам все равно придется использовать cudaMemcpy
, класс только инкапсулирует RAII), поэтому другой подход будет размещение new
:
float* device_data = new (cudaDevice) float[SIZE];
// Use `device_data` …
operator delete [](device_data, cudaDevice);
Здесь cudaDevice
просто выступает в качестве тега для запуска перегрузки. Однако, поскольку в обычном размещении new
это указывает на размещение, я считаю, что синтаксис нечетно согласован и, возможно, даже предпочтительнее использовать класс.
Буду признателен за любую критику. Может кто-нибудь, возможно, знает, запланировано ли что-то в этом направлении для следующей версии CUDA (которая, как я слышал, улучшит ее поддержку на С++, что бы это ни значило).
Итак, мой вопрос на самом деле трижды:
- Является ли мое размещение
new
перегруженным семантически правильным? Это утечка памяти? - Есть ли у кого-нибудь информация о будущих разработках CUDA, которые идут в этом общем направлении (давайте посмотрим на это: интерфейсы C в С++ s * ck)?
- Как я могу сделать это дальше последовательно (есть другие API-интерфейсы для рассмотрения, например, там не только память устройства, но также постоянное хранилище и память текстур)?
// 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&);
};
О синглтоне, используемом здесь: Да, я знаю о его недостатках. Однако в этом контексте они не актуальны. Все, что мне было нужно, это тег небольшого типа, который не был скопирован. Все остальное (т.е. Многопоточность, время инициализации) не применяются.