CUDA: agrupando a alocação de memória do dispositivo em C ++
Estou começando a usar o CUDA no momento e tenho que admitir que estou um pouco decepcionado com a API C. Entendo os motivos da escolha C, mas, se a linguagem tivesse sido baseada em C ++, vários aspectos seriam muito mais simples, por exemplo, alocação de memória do dispositivo (viacudaMalloc
)
Meu plano era fazer isso sozinho, usando sobrecarregadooperator new
com colocaçãonew
e RAII (duas alternativas). Eu estou querendo saber se há alguma ressalva que eu não notei até agora. O códigoparece para trabalhar, mas ainda estou pensando em possíveis vazamentos de memória.
O uso doRAII código seria o seguinte:
CudaArray<float> device_data(SIZE);
// Use `device_data` as if it were a raw pointer.
Talvez uma classe seja um exagero nesse contexto (especialmente porque você ainda precisará usarcudaMemcpy
, a classe apenas encapsula RAII), portanto a outra abordagem seriaposicionamentonew
:
float* device_data = new (cudaDevice) float[SIZE];
// Use `device_data` …
operator delete [](device_data, cudaDevice);
Aqui,cudaDevice
apenas atua como uma tag para acionar a sobrecarga. No entanto, como no posicionamento normalnew
isso indicaria o posicionamento, acho a sintaxe estranhamente consistente e talvez até preferível a usar uma classe.
Eu apreciaria críticas de todo tipo. Alguém talvez saiba se algo nessa direção está planejado para a próxima versão do CUDA (que, como ouvi dizer, melhorará o suporte a C ++, seja lá o que eles querem dizer com isso).
Então, minha pergunta é na verdade tripla:
É a minha veiculaçãonew
sobrecarga semanticamente correta? Isso vaza memória?Alguém tem informações sobre futuros desenvolvimentos CUDA que seguem nessa direção geral (vamos ser sinceros: interfaces C em C ++ s * ck)?Como posso levar isso adiante de maneira consistente (existem outras APIs a serem consideradas, por exemplo, não apenas a memória do dispositivo, mas também um armazenamento de memória constante e memória de textura)?// 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&);
};
Sobre o singleton empregado aqui: Sim, estou ciente de suas desvantagens. No entanto, estes não são relevantes neste contexto. Tudo o que eu precisava aqui era de uma pequena marca de tipo que não fosse copiável. Todo o resto (ou seja, considerações sobre multithreading, hora da inicialização) não se aplica.