2

CUDAメモリ構造の.NETラッパーとして機能するクラス(以下の例を参照)
があり 、cudaMalloc()を使用して割り当てられ、IntPtrタイプのメンバーフィールドを使用して参照されます。
(クラスは、さまざまなCUDA機能をラップするネイティブC DLLのDllImportを使用します。)

disposeメソッドは、ポインターがIntPtr.Zeroであるかどうかを確認し、そうでない場合は
、メモリの割り当てを正常に解除して(CUDAの成功を返す)cudaFree()を
呼び出します。 IntPtr.Zero。

finalizeメソッドはdisposeメソッドを呼び出します。

問題は、disposeが以前に呼び出されていない状態でfinalizeメソッドが呼び出された
場合、cudaFree()関数が「無効なデバイスポインタ」のエラーコードを設定することです。

チェックしたところ、cudaFree()が受け取るアドレスは、cudaMalloc()によって返されたアドレスと同じであり、dispose()は以前に呼び出されていません。

dispose()への明示的な呼び出しを追加すると、同じアドレスが正常に解放されます。

私が見つけた唯一の回避策は、ファイナライザーからdisposeメソッドを呼び出さないことでしたが、dispose()が常に呼び出されるとは限らない場合、これによりメモリリークが発生する可能性があります。

なぜこれが起こるのか考えはありますか?-WindowsVista64ビット+GeForce8800およびWindowsXP32ビット+QuadroFXの.NET3.5SP1で、CUDA 2.2および2.3で同じ問題が発生しました(どちらの番号かはわかりません)。

クラスCudaEntity:IDisposable
{{
    プライベートIntPtrdataPointer;

    public CudaEntity()
    {{
        // DllImportを介してcudaMalloc()を呼び出します。
        //エラーコードを受け取り、0でない場合は期待値をスローします
        //this.dataPointerに値を割り当てます
    }

    public Dispose()
    {{
        if(this.dataPointer!= IntPtr.Zero)
        {{
            // DllImportを介してcudaFree()を呼び出します。
            //エラーコードを受け取り、0でない場合は期待値をスローします

            this.dataPointer = IntPtr.Zero;
        }
    }

    〜CudaEntity()
    {{
        廃棄();
    }
}
{{
    //このコードは機能します
    var myEntity = new CudaEntity();
    myEntity.Dispose();
}
{{
    //このコードは「無効なデバイスポインタ」を引き起こします
    //ファイナライザーによるcudaFree()の呼び出しでエラーが発生しました
    var myEntity = new CudaEntity();
}
4

1 に答える 1

3

問題は、ファイナライザーがGCスレッドで実行され、あるスレッドで割り当てられたCUDAリソースを別のスレッドで使用できないことです。CUDAプログラミングガイドからの抜粋:

複数のホストスレッドが同じデバイスでデバイスコードを実行できますが、設計上、ホストスレッドは1つのデバイスでのみデバイスコードを実行できます。結果として、複数のデバイスでデバイスコードを実行するには、複数のホストスレッドが必要になります。また、あるホストスレッドのランタイムを通じて作成されたCUDAリソースは、別のホストスレッドのランタイムでは使用できません。

最善の策は、usingステートメントを使用することです。これにより、Dispose()メソッドは常に「保護された」コードブロックの最後で呼び出されます。

using(CudaEntity ent = new CudaEntity())
{

}
于 2009-09-19T23:52:04.970 に答える