【问题标题】:CUDA global memory deallocation issues in .NET.NET 中的 CUDA 全局内存释放问题
【发布时间】:2010-11-29 19:18:52
【问题描述】:

我有一个类 (参见下面的示例),它充当 CUDA 内存结构的 .NET 包装器,
使用 cudaMalloc() 分配并使用 IntPtr 类型的成员字段引用。
(该类使用本机 C DLL 的 DllImport 封装了各种 CUDA 功能。)

dispose 方法检查指针是否为 IntPtr.Zero,如果不是则调用 cudaFree()
成功释放内存(返回 CUDA 成功)
并将指针设置为 IntPtr.Zero。

finalize 方法调用 dispose 方法。

问题是,如果调用 finalize 方法而之前没有调用 dispose,
然后 cudaFree() 函数设置“无效设备指针”的错误代码。

我检查了一下,cudaFree() 接收到的地址与 cudaMalloc() 返回的地址相同,并且之前没有调用过 dispose()。

当我添加对 dispose() 的显式调用时,相同的地址被成功释放。

我发现的唯一解决方法是不要从终结器调用 dispose 方法,但是,如果并不总是调用 dispose(),这可能会导致内存泄漏。

任何想法为什么会发生这种情况? - 在 Windows Vista 64 位 + GeForce 8800 和 Windows XP 32 位 + Quadro FX 上的 .NET 3.5 SP1 下,我在 CUDA 2.2 和 2.3 上遇到了同样的问题(不确定是哪个数字)。

类 CudaEntity : IDisposable { 私有 IntPtr 数据指针; 公共 CudaEntity() { // 通过 DllImport 调用 cudaMalloc(), // 接收错误码,如果不为 0 则抛出期望 // 给 this.dataPointer 赋值 } 公共处置() { if (this.dataPointer != IntPtr.Zero) { // 通过 DllImport 调用 cudaFree(), // 接收错误码,如果不为 0 则抛出期望 this.dataPointer = IntPtr.Zero; } } ~CudaEntity() { 处置(); } } { //这段代码有效 var myEntity = new CudaEntity(); myEntity.Dispose(); } { // 此代码导致“无效的设备指针” // 终结器调用 cudaFree() 时出错 var myEntity = new CudaEntity(); }

【问题讨论】:

  • 如果免费失败,我不会抛出异常。 MS 指南建议避免这种情况。
  • 我只是补充说,以帮助调试问题,但是,由于某些未知原因,我在 VS 上工作的两台 PC 中的一台自动将返回代码视为异常,而我没有抛出一个!?跨度>

标签: .net interop idisposable cuda garbage-collection


【解决方案1】:

问题是终结器是在 GC 线程上执行的,在一个线程中分配的 CUDA 资源不能在另一个线程中使用。来自 CUDA 编程指南的片段:

多个宿主线程可以执行 同一设备上的设备代码,但通过 设计,一个宿主线程可以执行 仅在一台设备上的设备代码。作为一个 结果,多个主机线程是 需要执行设备代码 多个设备。此外,任何 CUDA 通过运行时创建的资源 在一个主机线程中不能被 来自另一个主机线程的运行时。

最好的办法是使用using 语句,它确保始终在“受保护”代码块的末尾调用Dispose() 方法:

using(CudaEntity ent = new CudaEntity())
{

}

【讨论】:

  • 谢谢,在大多数情况下,我不会在一个块中使用 CudaEntity,因此该解决方案在大多数情况下无济于事。我只需要检查所有代码以确保在覆盖 CudaEntity 或处理包含 CudaEntities 的对象时始终调用 dispose()。
  • +1。我发现当我需要使用来自多个线程的 CUDA 包装器对象时,最好的办法是在包装器类中保留一个私有 Thread 成员,并在该线程上运行所有 DllImport 调用,以隐藏线程- 来自客户端代码的相关性详细信息。
  • dispose时可以分派吗? - 我很确定 GC 会冻结所有其他线程,除非使用服务器 GC 模型。
  • 如何添加一个静态的 dispose 队列,然后在 finalize 中添加指向队列的指针并在下一次分配或 dispose 时,将队列中的所有内容都处理掉?
  • 是的,这可能有效。但是您需要通过分配/释放线程路由所有 CUDA 调用。这是一种骇人听闻的方法,而且过于防御性了。一旦不需要资源就释放它有什么问题?
猜你喜欢
  • 1970-01-01
  • 2015-01-28
  • 2019-09-10
  • 2014-01-10
  • 2012-05-06
  • 2014-11-17
  • 2016-09-21
  • 2013-11-09
相关资源
最近更新 更多