Domanda

Ho una lezione (vedi esempio sotto) che funge da wrapper .NET per una struttura di memoria CUDA,
allocato utilizzando cudaMalloc() e referenziato utilizzando un campo membro di tipo IntPtr.
(La classe utilizza DllImport di una DLL C nativa che racchiude varie funzionalità CUDA.)

Il metodo Dispose controlla se il puntatore è IntPtr.Zero e in caso contrario chiama cudaFree()
che dealloca con successo la memoria (restituisce il successo CUDA)
e imposta il puntatore su IntPtr.Zero.

Il metodo finalize chiama il metodo Dispose.

Il problema è che se il metodo finalize viene chiamato senza che il metodo Dispose sia stato chiamato in precedenza,
quindi la funzione cudaFree() imposta un codice di errore di "puntatore dispositivo non valido".

Ho controllato e l'indirizzo ricevuto da cudaFree() è lo stesso indirizzo restituito da cudaMalloc() e non è stato chiamato in precedenza nessun disporre().

Quando aggiungo una chiamata esplicita a Dispose() lo stesso indirizzo viene liberato con successo.

L'unica soluzione che ho trovato è stata quella di non chiamare il metodo Dispose dal finalizzatore, tuttavia, ciò potrebbe causare una perdita di memoria, se Dispose() non viene sempre chiamato.

Qualche idea sul perché questo accada?- Ho riscontrato lo stesso problema con CUDA 2.2 e 2.3, in .NET 3.5 SP1 su Windows Vista a 64 bit + GeForce 8800 e su Windows XP a 32 bit + Quadro FX (non sono sicuro di quale numero).

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();
}
È stato utile?

Soluzione

Il problema è che i finalizzatori vengono eseguiti sul thread GC, la risorsa CUDA allocata in un thread non può essere utilizzata in un altro.Un estratto dalla guida alla programmazione CUDA:

Diversi thread host possono eseguire il codice del dispositivo sullo stesso dispositivo, ma in base alla progettazione, un thread host può eseguire il codice del dispositivo su un solo dispositivo.Di conseguenza, sono necessari più thread host per eseguire il codice del dispositivo su più dispositivi.Inoltre, qualsiasi risorsa CUDA creata attraverso il runtime in un thread host non può essere utilizzata dal runtime da un altro thread host.

La soluzione migliore è utilizzare il file using dichiarazione, che garantisce che il Dispose() il metodo viene sempre chiamato alla fine del blocco di codice 'protetto':

using(CudaEntity ent = new CudaEntity())
{

}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top