释放OpenCL内存、内核、设备等。

6

我记得在某个地方看到过(但是找不到来源),使用C++ API时,您不必像使用C API一样释放设备/内核/内存,因为当类对象超出范围时(程序结束等),cl::Kernel、cl::Buffer、cl::Device的析构函数会自动处理这些。然而,在仔细检查最新的cl.hpp文件(1.1 rev 04)后,我发现没有定义任何析构函数。例如,这里是cl::Device -

/*! \class Device
 * \brief Device interface for cl_device_id.
 */
class Device : public detail::Wrapper<cl_device_id>
{
public:
    Device(cl_device_id device) { object_ = device; }

    Device() : detail::Wrapper<cl_type>() { }

    Device(const Device& device) : detail::Wrapper<cl_type>(device) { }

    Device& operator = (const Device& rhs)
    {
        if (this != &rhs) {
            detail::Wrapper<cl_type>::operator=(rhs);
        }
        return *this;
    }

    template <typename T>
    cl_int getInfo(cl_device_info name, T* param) const
    {
        return detail::errHandler(
            detail::getInfo(&::clGetDeviceInfo, object_, name, param),
            __GET_DEVICE_INFO_ERR);
    }

    template <cl_int name> typename
    detail::param_traits<detail::cl_device_info, name>::param_type
    getInfo(cl_int* err = NULL) const
    {
        typename detail::param_traits<
            detail::cl_device_info, name>::param_type param;
        cl_int result = getInfo(name, &param);
        if (err != NULL) {
            *err = result;
        }
        return param;
    }

#if defined(USE_CL_DEVICE_FISSION)
    cl_int createSubDevices(
        const cl_device_partition_property_ext * properties,
        VECTOR_CLASS<Device>* devices)
    {
        typedef CL_API_ENTRY cl_int 
            ( CL_API_CALL * PFN_clCreateSubDevicesEXT)(
                cl_device_id /*in_device*/,
                const cl_device_partition_property_ext * /* properties */,
                cl_uint /*num_entries*/,
                cl_device_id * /*out_devices*/,
                cl_uint * /*num_devices*/ ) CL_EXT_SUFFIX__VERSION_1_1;

        static PFN_clCreateSubDevicesEXT pfn_clCreateSubDevicesEXT = NULL;
        __INIT_CL_EXT_FCN_PTR(clCreateSubDevicesEXT);

        cl_uint n = 0;
        cl_int err = pfn_clCreateSubDevicesEXT(object_, properties, 0, NULL, &n);
        if (err != CL_SUCCESS) {
            return detail::errHandler(err, __CREATE_SUB_DEVICES);
        }

        cl_device_id* ids = (cl_device_id*) alloca(n * sizeof(cl_device_id));
        err = pfn_clCreateSubDevicesEXT(object_, properties, n, ids, NULL);
        if (err != CL_SUCCESS) {
            return detail::errHandler(err, __CREATE_SUB_DEVICES);
        }

        devices->assign(&ids[0], &ids[n]);
        return CL_SUCCESS;
    }
#endif
};

有人知道这个吗?我需要关注这个吗?在C++包装器文档中,他们甚至没有提到类似于releaseClMemObject或free(cl_devices)等内容。

谢谢。

3个回答

12
如果您仔细查看,就会发现所有这些类都是从一个名为detail::Wrapper<T>的模板继承而来的,而这个模板针对每一种类型进行了专门化处理,以确实在其析构函数中调用相应的clRelease...函数。如您所知,类的析构函数总是会调用其基类的析构函数,因此在cl::Buffercl::Kernel等类中不需要用户定义析构函数。(准确地说,Wrapper<T>没有被专门化,而是使用了其他名为ReferenceHandler<T>的专门化特性类,该类引入了retainrelease函数。)
因此,由于所有这些OpenCL对象都使用某种引用计数语义,并且所有这些C++包装器在其构造函数和析构函数中包装相应的clRetain/clRelease调用,因此在使用C++时确实不必担心正确释放OpenCL资源,只需像RAII方式一样即可。
(但正如DarkZeros已经提到的那样,设备可能是一个糟糕的例子,因为设备通常不会被保留或释放(detail::Wrapper<cl_device_id>的构造/析构函数可能什么也不做)。随着OpenCL 1.2的设备分裂,它们可能会被保留或释放,但C++包装器无论如何都不支持1.2版本。)

非常感谢您的回复。不过,我之前查看了源文件,没有在Wrapper<T>或者其父类(例如memory/context)中找到release calls或destructor functions。您能提供一下具体文件和代码行数吗?关于device这个问题,我只在C语言中见过它。我对device fission并不熟悉,但听起来很有趣。 - Steve Novakov
1
@SteveNovakov 在 Wrapper<T> 的析构函数中(第1103行),你可以看到它调用了 Wrapper<T>::release,而这个函数(第1133行)又调用了 ReferenceHandler<T>::release。在1000-1087行之间,你可以看到 ReferenceHandler<T> 及其各种不同的 OpenCL 对象类型的特化版本。(希望行号匹配,我有来自 Khronos 的官方头文件。当然,这个文件是 cl.hpp,是 C++ 包装器的唯一文件,所有其他文件都是为 C 接口而设计的。) - Christian Rau
我完全不知道我第一次阅读时是怎么忽略了那个。非常感谢你。 - Steve Novakov

4
在OpenCL中,唯一需要释放的是抽象构造,如下:
  • 内存对象
  • 上下文
  • 命令队列
  • 程序
  • 内核
  • 事件
你不能释放设备,因为设备无法“销毁”或未分配。当您调用 getDevice 时,您会收到一个设备 ID,而不是新创建的设备。
注意:在OCL 1.2 中,设备可以构造为上级设备的分区(子设备)。因此,它应该有一个删除情况。也许 CL API 应该处理这些新版本的特定情况……我不知道。

1
只需使用


clReleaseMemObject(your_buffer());

秘密在于使用cl::Buffer或其他类时,在your_buffer名称后面加上()。将相同的逻辑/语法应用于其他情况。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接