Проблемы освобождения памяти CUDA в .NET - PullRequest
2 голосов
/ 20 сентября 2009

У меня есть класс (см. Пример ниже) , который действует как оболочка .NET для структуры памяти CUDA,
выделяется с помощью cudaMalloc () и ссылается с помощью поля члена типа IntPtr.
(Класс использует DllImport нативной C-библиотеки DLL, которая включает в себя различные функции CUDA.)

Методы dispose проверяют, является ли указатель IntPtr.Zero и, если нет, вызывает cudaFree ()
который успешно освобождает память (возвращает CUDA success)
и устанавливает указатель на IntPtr.Zero.

Метод finalize вызывает метод dispose.

Проблема состоит в том, что если методы finalize вызываются без вызова dispose, вызываемого ранее,
затем функция cudaFree () устанавливает код ошибки «недопустимый указатель устройства».

Я проверил, и адрес, который получает cudaFree (), совпадает с адресом, возвращаемым cudaMalloc (), и никакой dispose () не был вызван ранее.

Когда я добавляю вызов explict для dispose (), тот же адрес успешно освобождается.

Единственный найденный мной обходной путь - не вызывать метод dispose из финализатора, однако это может привести к утечке памяти, если dispose () вызывается не всегда.

Есть идеи, почему это происходит? - Я столкнулся с той же проблемой с CUDA 2.2 и 2.3, в .NET 3.5 SP1 в Windows Vista 64bit + GeForce 8800 и в Windows XP 32bit + Quadro FX (не знаю, какое число).

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();
}

1 Ответ

3 голосов
/ 20 сентября 2009

Проблема в том, что финализаторы выполняются в потоке GC, ресурс CUDA, выделенный в одном потоке, не может использоваться в другом. Отрывок из руководства по программированию CUDA:

Может выполняться несколько потоков хоста код устройства на том же устройстве, но дизайн, поток узла может выполнить код устройства только на одном устройстве. Как Как следствие, несколько потоков хоста требуется выполнить код устройства на несколько устройств. Также любая CUDA ресурсы, созданные во время выполнения в одном хосте поток не может быть использован время выполнения из другого потока хоста.

Лучше всего использовать оператор using, который гарантирует, что метод Dispose() всегда вызывается в конце «защищенного» блока кода:

using(CudaEntity ent = new CudaEntity())
{

}
...