2008-11-18 12 views
17

Estoy empezando a utilizar CUDA por el momento y tengo que admitir que estoy un poco decepcionado con la API C. Entiendo las razones para elegir C, pero el lenguaje se basó en C++, varios aspectos hubieran sido mucho más simples, por ej. asignación de memoria del dispositivo (a través de cudaMalloc).CUDA: asignación de memoria del dispositivo de empaquetado en C++

Mi plan era hacer esto yo mismo, utilizando operator new sobrecargado con la colocación new y RAII (dos alternativas). Me pregunto si hay algunas advertencias que no haya notado hasta ahora. El código parece para funcionar, pero todavía me pregunto acerca de posibles pérdidas de memoria.

El uso del código RAII sería la siguiente:

CudaArray<float> device_data(SIZE); 
// Use `device_data` as if it were a raw pointer. 

Tal vez una clase es una exageración en este contexto (sobre todo porque todavía habría que utilizar cudaMemcpy, la única clase que encapsula RAII) por lo que el otro enfoque sería colocación new:

float* device_data = new (cudaDevice) float[SIZE]; 
// Use `device_data` … 
operator delete [](device_data, cudaDevice); 

Aquí, cudaDevice simplemente actúa como una etiqueta para desencadenar la sobrecarga. Sin embargo, dado que en la colocación normal new esto indicaría la ubicación, creo que la sintaxis es extrañamente consistente y tal vez incluso preferible al uso de una clase.

Agradecería las críticas de todo tipo. ¿Alguien quizás sabe si se planea algo en esta dirección para la próxima versión de CUDA (que, como he escuchado, mejorará su compatibilidad con C++, lo que sea que signifiquen con eso).

Por lo tanto, mi pregunta es en realidad tres:

  1. ¿Es mi sobrecarga de colocación new semánticamente correcto? ¿Pierde memoria?
  2. ¿Alguien tiene información sobre futuros desarrollos de CUDA que van en esta dirección general (seamos sinceros: C interfaces en C++ s * ck)?
  3. ¿Cómo puedo llevar esto más allá de manera consistente (hay otras API a tener en cuenta, por ejemplo, no solo hay memoria del dispositivo, sino también un almacén de memoria constante y memoria 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 el producto único empleado aquí: Sí, soy consciente de sus inconvenientes. Sin embargo, estos no son relevantes en este contexto. Todo lo que necesitaba aquí era una pequeña etiqueta de tipo que no se podía copiar. No se aplica todo lo demás (es decir, consideraciones de subprocesamiento múltiple, tiempo de inicialización).

+1

Su implementación de singleton es peligrosa en el mejor de los casos. Consulte las muchas otras discusiones sobre cómo crear un singleton en C++. –

+0

Sí, tienes razón. Sin embargo, vea mi nueva aclaración debajo del código. –

Respuesta

5

Me gustaría ir con el nuevo enfoque de colocación. Luego definiría una clase que se ajuste a la interfaz std :: allocator <>. En teoría, podría pasar esta clase como un parámetro de plantilla en std :: vector <> y std :: map <> y así sucesivamente.

Cuidado, he oído que hacer tales cosas está plagado de dificultades, pero al menos aprenderá mucho más sobre el STL de esta manera. Y no necesita reinventar sus contenedores y algoritmos.

+0

No había pensado en un asignador. De hecho, he hecho esto antes, así que no debería ser demasiado difícil. –

2

Hay varios proyectos que intentan algo similar, por ejemplo CUDPP.

Mientras tanto, sin embargo, he implementado mi propio asignador y funciona bien y fue sencillo (> código repetitivo del 95%).

+0

El enlace stdcuda está muerto. – einpoklum

+0

@einpoklum Gracias. Es lógico pensar que una respuesta de 10 años en algún momento estaría desactualizada. He eliminado el enlace. –

7

Mientras tanto hubo algunos desarrollos adicionales (no tanto en términos de la API de CUDA, sino al menos en términos de proyectos que intentan un enfoque de tipo STL para la gestión de datos de CUDA).

Lo más notable es que hay un proyecto de investigación de NVIDIA: thrust

1

¿Alguien tiene información sobre futuros desarrollos CUDA que van en esta dirección general (seamos sinceros: las interfaces de C en C++ s * ck)?

algo Sí, lo he hecho así:

https://github.com/eyalroz/cuda-api-wrappers/

API de tiempo de ejecución de Nvidia CUDA es para uso tanto en código C y C++. Como tal, utiliza una API de estilo C, el denominador común más bajo (con algunas excepciones notables de sobrecargas de funciones con plantilla).

Esta biblioteca de envolturas alrededor de la API de tiempo de ejecución está destinada a permitir que abracemos muchas de las características de C++ (incluyendo algunos de C++ 11) para el uso de la API de tiempo de ejecución -, pero sin reducir la expresividad o aumentar el nivel de abstracción (como en, por ejemplo, la biblioteca Thrust). Usando cuda-api-wrappers, todavía tiene sus dispositivos, transmisiones, eventos, etc., pero será más conveniente trabajar con más C++ de forma idiomática.

Cuestiones relacionadas