【问题标题】:Free memory of a GpuMatGpuMat 的空闲内存
【发布时间】:2018-08-21 03:31:02
【问题描述】:

我的 CUDA 设备内存不足。我已经检测到原因 - 在我的一些成员函数中,我返回了一个 GpuMat,该 GpuMat 是根据我自己分配的数据构建的。这里的问题是 - 我如何释放这个内存?

class Test {
  GpuMat test() {
    float* dev_ptr = nullptr;
    cv::Size size(8192,8192);
    cudaMalloc((void**)&dev_ptr, size.width*size.height*sizeof(float));
    return GpuMat(size, CV_32FC1, dev_ptr);
  }
}
//main
Test t;
while(true) {
  size_t free_mem, total_mem;
  cudaMemGetInfo(&free_mem, &total_mem);
  std::cout << free_mem << std::endl;
  cv::namedWindow("test", CV_WINDOW_OPENGL | CV_WINDOW_NORMAL);
  cv::imshow("test", t.test());
  cv::waitKet(1);
}

您会看到每次迭代中free_mem 的数量会减少(减少相同的数量)。


我尝试过创建自定义分配器和setDefaultAllocator,但没有成功。

class CustomAllocator : public cv::cuda::GpuMat::Allocator
{
    float* m_ptr;
public:
    CustomAllocator(float* p) : cv::cuda::GpuMat::Allocator(), m_ptr(p) {}
    virtual bool allocate(cv::cuda::GpuMat* mat, int rows, int cols, size_t elemsize) override
    {
        return true;
    }
    virtual void free(cv::cuda::GpuMat* mat) override
    {
        cudaFree(m_ptr);
    }
};
// more or less looked like this...
GpuMat test() {
    float* dev_ptr = nullptr;
    cv::Size size(8192,8192);
    cudaMalloc((void**)&dev_ptr, size.width*size.height*sizeof(float));
    GpuMat retval(size, CV_32FC1, dev_ptr);
    retval.setDefaultAllocator(new CustomAllocator(dev_ptr));
    return retval;
  }

AllocatorGpuMat 的文档都写得不太好,因此我们将不胜感激。


什么似乎有效

GpuMat test() {
  float* dev_ptr = nullptr;
  cv::Size size(8192,8192);
  cudaMalloc((void**)&dev_ptr, size.width*size.height*sizeof(float));
  // do processing...
  // clone data so the GpuMat manages it by itself
  auto retval = GpuMat(size, CV_32FC1, dev_ptr).clone();
  // free manually allocated memory
  cudaFree(dev_ptr);
  return retval;
}

正如我现在写下来的那样,我认为最佳方案是首先分配 GpuMat,然后使用其 data 分配 //do processing...

【问题讨论】:

  • 您正在试验的所有机制都依赖于超出范围的对象并触发类析构函数。如果你不明白这意味着什么,那么你需要修改 C++ 对象模型中的一些非常核心的概念,然后再继续深入
  • 好的,你让我明白了,因为我确实认为 test() 返回的 GpuMat 的范围仅限于一次 while 循环迭代。老实说,我仍然不确定为什么不是那样...你能详细说明一下吗?
  • 您正在 while 循环中创建类的实例。如果您通过对象构造和销毁来实现正确的分配和解除分配(并且您还没有回答),则对象需要超出范围才能触发销毁。这意味着循环退出后,将为循环内的所有对象释放内存。我不认为这是你想要的行为
  • 当然,您可以在循环内定义额外的范围以在每次循环行程后强制销毁

标签: opencv memory-management cuda out-of-memory gpu


【解决方案1】:

您正在使用涉及用户提供分配的特定 GpuMat 变体。此外,t 永远不会超出您的 while 循环的范围,所以我不清楚任何隐式方法都可以工作。

由于您使用cudaMalloc 提供内存分配,我的建议是您应该使用cudaFree 释放它。因此,简单地说,使dev_ptr 成为类数据成员而不是立即/临时变量,并提供一个Test::finish() 成员函数来测试该指针是否为非NULL,如果是,则在其上运行cudaFree()。当然,如果您想通过构造函数/析构函数进行处理,您可能会采用不同的方式,并且可能有几十种变化。

这是一种可能的方法,在浏览器中编码,未经测试:

class Test {
  float* dev_ptr;
  public:
    Test() {dev_ptr = nullptr;}
    void finish() { if (dev_ptr != nullptr) cudaFree(dev_ptr);}
    GpuMat test() {
      cv::Size size(8192,8192);
      cudaMalloc((void**)&dev_ptr, size.width*size.height*sizeof(float));
      return GpuMat(size, CV_32FC1, dev_ptr);
    }
}
//main
Test t;
while(true) {
  size_t free_mem, total_mem;
  cudaMemGetInfo(&free_mem, &total_mem);
  std::cout << free_mem << std::endl;
  cv::namedWindow("test", CV_WINDOW_OPENGL | CV_WINDOW_NORMAL);
  cv::imshow("test", t.test());
  cv::waitKey(1);
  t.finish();
}

或者,如果您想避免显式调用finish(),而只是想重新分配,您可以这样做:

class Test {
  float* dev_ptr;
  public:
    Test() {dev_ptr = nullptr;}
    GpuMat test() {
      cv::Size size(8192,8192);
      if (dev_ptr != nullptr) cudaFree(dev_ptr);
      cudaMalloc((void**)&dev_ptr, size.width*size.height*sizeof(float));
      return GpuMat(size, CV_32FC1, dev_ptr);
    }
}
//main
Test t;
while(true) {
  size_t free_mem, total_mem;
  cudaMemGetInfo(&free_mem, &total_mem);
  std::cout << free_mem << std::endl;
  cv::namedWindow("test", CV_WINDOW_OPENGL | CV_WINDOW_NORMAL);
  cv::imshow("test", t.test());
  cv::waitKey(1);
}

【讨论】:

  • 因为我的类中有更多的方法,比如 test() (upsss),我宁愿有一个数据感知容器,它会在销毁时释放内存并且没有成员指针。这就是为什么我想尝试使用自定义分配器来处理它。因为我猜最终返回的 GpuMat 在迭代时超出了范围,对吗?是否可以选择将有问题的指针限制为 GpuMat 寿命?作为这种想法的一些背景解释,“测试”类在 GPU 上分配了一些数据,我想返回一些处理的结果。
猜你喜欢
  • 1970-01-01
  • 2012-09-18
  • 2015-08-17
  • 2018-06-06
  • 1970-01-01
  • 1970-01-01
  • 2021-02-27
  • 2016-12-24
  • 2011-07-13
相关资源
最近更新 更多