Question

Je commence à utiliser CUDA pour le moment et je dois admettre que je suis un peu déçu de l’API C. Je comprends les raisons du choix de C, mais si le langage avait été basé sur C ++, plusieurs aspects auraient été beaucoup plus simples, par exemple. allocation de mémoire de l'appareil (via cudaMalloc).

Mon plan était de le faire moi-même, en utilisant operator new surchargé avec le placement new et le RAII (deux alternatives). Je me demande s'il y a des mises en garde que je n'ai pas remarquées jusqu'à présent. Le code semble fonctionner mais je m'interroge toujours sur les fuites de mémoire potentielles.

L'utilisation du code RAII serait la suivante:

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

Peut-être qu'une classe surpasse dans ce contexte (surtout que vous devez toujours utiliser cudaMemcpy, la classe n'encapsulant que la RAII), de sorte que l'autre approche serait placement cudaDevice :

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

Ici, <=> agit simplement comme une balise pour déclencher la surcharge. Cependant, étant donné que dans un placement normal <=> cela indiquerait le placement, je trouve la syntaxe étrangement cohérente et peut-être même préférable à l'utilisation d'une classe.

J'apprécierais les critiques de toutes sortes. Est-ce que quelqu'un sait peut-être si quelque chose dans cette direction est prévu pour la prochaine version de CUDA (qui, comme je l'ai entendu dire, améliorera son support C ++, quoi qu'ils entendent par là).

Donc, ma question est en fait triple:

  1. Mon emplacement <=> est-il sémantiquement correct? Est-ce qu'il perd de la mémoire?
  2. Quelqu'un at-il des informations sur les développements futurs de CUDA qui vont dans cette direction (avouons-le: les interfaces C en C ++ sont * ck)?
  3. Comment puis-je aller plus loin dans cette démarche de manière cohérente (il y a d'autres API à prendre en compte, par exemple, il n'y a pas seulement de la mémoire de périphérique, mais également une mémoire de stockage constante et une mémoire de texture)?
// 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&);
};

À propos du singleton employé ici: Oui, je suis conscient de ses inconvénients. Cependant, ceux-ci ne sont pas pertinents dans ce contexte. Tout ce dont j'avais besoin ici était une petite étiquette de type qui ne pouvait pas être copiée. Tout le reste (considérations multithreading, heure d'initialisation) ne s'applique pas.

Était-ce utile?

La solution

Je voudrais aller avec la nouvelle approche de placement. Ensuite, je définirais une classe conforme à std :: allocator & Lt; & Gt; interface. En théorie, vous pourriez passer cette classe en tant que paramètre de modèle dans std :: vector & Lt; & Gt; et std :: map < > et ainsi de suite.

Attention, j'ai entendu dire que cela posait de nombreux problèmes, mais au moins, vous en apprendrez beaucoup plus sur le TSL de cette façon. Et vous n'avez pas besoin de réinventer vos conteneurs et algorithmes.

Autres conseils

Entre temps, il y a eu quelques développements supplémentaires (pas tellement en termes d’API CUDA, mais au moins en termes de projets essayant une approche de type gestion de données CUDA de type STL).

Il existe notamment un projet de recherche NVIDIA: poussée

  

Quelqu'un at-il des informations sur les développements futurs de CUDA qui vont dans cette direction générale (avouons-le: interfaces C en C ++ s * ck)?

Oui, j'ai fait quelque chose comme ça:

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

  

L’API d’exécution de nVIDIA pour CUDA est conçue pour être utilisée à la fois dans le code C et C ++. En tant que tel, il utilise une API de style C, le plus petit dénominateur commun (à quelques exceptions notables des surcharges de fonctions basées sur un modèle).

     

Cette bibliothèque d'encapsuleurs autour de l'API d'exécution a été conçue pour nous permettre d'exploiter un grand nombre des fonctionnalités de C ++ (y compris certaines C ++ 11) permettant d'utiliser l'API d'exécution - mais sans réduire l'expressivité ni augmenter le niveau d'abstraction dans, par exemple, la bibliothèque Thrust). En utilisant cuda-api-wrappers, vous avez toujours vos appareils, flux, événements, etc., mais ils seront plus pratiques à utiliser de manière plus C ++ - idiomatique.

Plusieurs projets tentent quelque chose de similaire, par exemple CUDPP .

Entre-temps, cependant, j’ai mis en place mon propre allocateur, qui fonctionne bien et qui est simple (> 95% de code passe-partout).

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top