2009-09-19 11 views
2

j'ai une classe (voir exemple ci-dessous) qui agit comme une enveloppe .NET pour une structure de mémoire CUDA,
alloué à l'aide cudaMalloc() et fait référence à l'aide d'un champ de membre de type IntPtr.
(La classe utilise DllImport d'une DLL C native qui enveloppe diverses fonctionnalités de CUDA.)

Les méthodes Dispose vérifie si le pointeur est IntPtr.Zero et si pas les appels cudaFree()
qui désalloue avec succès la mémoire (retours Succès CUDA)
et définit le pointeur sur IntPtr.Zero.

La méthode finalize appelle la méthode dispose.

Le problème est que, si les méthodes Finalize est appelé avec le disposer appelé précédemment,
alors la fonction cudaFree() définit un code d'erreur de « pointeur de périphérique non valide ».

J'ai vérifié et l'adresse que cudaFree() reçoit est la même adresse retournée par le cudaMalloc() et aucune disposition() n'a été appelée précédemment. Lorsque j'ajoute un appel explict à disposer(), la même adresse est libérée avec succès.



La seule solution de contournement que j'ai trouvée était de ne pas appeler la méthode de disposition du finaliseur, cependant, cela pourrait causer une fuite de mémoire, si la méthode dispose() n'est pas toujours appelée.

Des idées pour lesquelles cela se produit? - J'ai rencontré le même problème avec CUDA 2.2 et 2.3, sous .NET 3.5 SP1 sur Windows Vista 64bit + GeForce 8800 et sur Windows XP 32bit + Quadro FX (je ne sais pas quel numéro).
CUDA problèmes mondiaux de désallocation de mémoire dans .NET

 
class CudaEntity : IDisposable 
{ 
    private IntPtr dataPointer; 

    public CudaEntity() 
    { 
     // Calls cudaMalloc() via DllImport, 
     // receives error code and throws expection if not 0 
     // assigns value to this.dataPointer 
    } 

    public Dispose() 
    { 
     if (this.dataPointer != IntPtr.Zero) 
     { 
      // Calls cudaFree() via DllImport, 
      // receives error code and throws expection if not 0 

      this.dataPointer = IntPtr.Zero; 
     } 
    } 

    ~CudaEntity() 
    { 
     Dispose(); 
    } 
} 
 
{ 
    // this code works 
    var myEntity = new CudaEntity(); 
    myEntity.Dispose(); 
} 
 
{ 
    // This code cause a "invalid device pointer" 
    // error on finalizer's call to cudaFree() 
    var myEntity = new CudaEntity(); 
} 
+0

Je ne jette une exception si le gratuit a échoué. Les lignes directrices sur la SP suggèrent d'éviter cela. – TrueWill

+0

J'ai seulement ajouté que pour aider au débogage du problème, cependant, pour une raison inconnue sur l'un des 2 PC que j'ai travaillé sur VS traite automatiquement le code retour comme une exception sans que j'en lève un! –

Répondre

3

Le problème est que finaliseurs sont exécutées sur le thread GC, CUDA ressources allouées dans un thread ne peut pas être utilisé dans un autre. Un petit bout du guide de programmation CUDA:

Plusieurs threads hôte peut exécuter du code de l'appareil sur le même appareil, mais par conception, un fil hôte peut exécuter du code de l'appareil sur un seul appareil. En conséquence , plusieurs threads hôte sont requis pour exécuter le code de périphérique sur plusieurs périphériques. En outre, toutes les ressources CUDA créées au cours de l'exécution dans un thread hôte ne peuvent pas être utilisées par à partir d'un autre thread hôte.

Votre meilleur pari est d'utiliser l'instruction using, qui assure que la méthode Dispose() obtient toujours appelé à la fin du bloc de code « protégé »:

using(CudaEntity ent = new CudaEntity()) 
{ 

} 
+0

Merci, Dans la plupart des cas, je n'utilise pas le CudaEntity dans un bloc, de sorte que la solution ne sera pas utile dans la plupart des cas. Je vais juste devoir inspecter tout le code pour m'assurer que la méthode dispose() est toujours appelée lors de l'écrasement d'un CudaEntity ou quand un objet contenant CudaEntities est éliminé. –

+0

+1. J'ai trouvé que lorsque j'ai besoin d'utiliser un objet encapsuleur CUDA à partir de plusieurs threads, le mieux est de garder un membre Thread privé dans la classe wrapper, et d'exécuter tous les appels DllImport sur ce thread, afin de cacher le thread détails d'affinité du code client. – Gabriel

+0

Est-il possible d'expédier pendant l'élimination? - Je suis à peu près sûr que le GC gèle tous les autres threads à moins d'utiliser le modèle GC du serveur. –