静态openCL类没有使用boost.python在python模块中正确发布

NOh*_*Ohs 13 c++ python boost opencl boost-python

编辑:好的,所有的编辑使问题的布局有点混乱,所以我将尝试重写问题(不改变内容,但改进其结构).

这个问题简而言之

如果我将它编译为可执行文件,我有一个openCL程序可以正常工作.现在我尝试使用Python来调用它boost.python.但是,只要我退出Python(导入我的模块后),python就会崩溃.

原因似乎与某些事情有关

程序终止时,仅静态存储GPU CommandQueues及其释放机制

MWE和设置

建立

  • 使用的IDE:Visual Studio 2015

  • 使用的操作系统:Windows 7 64位

  • Python版本:3.5

  • AMD OpenCL APP 3.0标头

  • cl2.hpp直接来自Khronos,如下所示:空openCL程序抛出弃用警告

  • 此外,我有一个带有集成图形硬件的Intel CPU,没有其他专用显卡

  • 我使用版本1.60的boost库编译为64位版本

  • 我使用的boost dll称为: boost_python-vc140-mt-1_60.dll

  • 没有python的openCL程序运行正常

  • 没有openCL的python模块工作正常

MWE

#include <vector>

#define CL_HPP_ENABLE_EXCEPTIONS
#define CL_HPP_TARGET_OPENCL_VERSION 200
#define CL_HPP_MINIMUM_OPENCL_VERSION 200 // I have the same issue for 100 and 110
#include "cl2.hpp"
#include <boost/python.hpp>

using namespace std;

class TestClass
{
private:
    std::vector<cl::CommandQueue> queues;
    TestClass();

public:
    static const TestClass& getInstance()
    {
        static TestClass instance;
        return instance;
    }
};

TestClass::TestClass()
{
    std::vector<cl::Device> devices;
    vector<cl::Platform> platforms;

    cl::Platform::get(&platforms);

    //remove non 2.0 platforms (as suggested by doqtor)
    platforms.erase(
        std::remove_if(platforms.begin(), platforms.end(),
            [](const cl::Platform& platform)
    {
        int v = cl::detail::getPlatformVersion(platform());
        short version_major = v >> 16;
        return !(version_major >= 2);
    }),
        platforms.end());

    //Get all available GPUs
    for (const cl::Platform& pl : platforms)
    {
        vector<cl::Device> plDevices;
        try {
            pl.getDevices(CL_DEVICE_TYPE_GPU, &plDevices);
        }
        catch (cl::Error&)
        {

            // Doesn't matter. No GPU is available on the current machine for 
            // this platform. Just check afterwards, that you have at least one
            // device
            continue;
        }       
        devices.insert(end(devices), begin(plDevices), end(plDevices));
    }

    cl::Context context(devices[0]);
    cl::CommandQueue queue(context, devices[0]);

    queues.push_back(queue);
}

int main()
{
    TestClass::getInstance();

    return 0;
}

BOOST_PYTHON_MODULE(FrameWork)
{
    TestClass::getInstance();
}
Run Code Online (Sandbox Code Playgroud)

调用程序

所以编译程序后dll我启动python并运行以下程序

import FrameWork
exit()
Run Code Online (Sandbox Code Playgroud)

虽然导入工作没有问题,python崩溃了exit().所以我点击调试,Visual Studio告诉我在下面的代码部分(in cl2.hpp)中有一个异常:

template <>
struct ReferenceHandler<cl_command_queue>
{
    static cl_int retain(cl_command_queue queue)
    { return ::clRetainCommandQueue(queue); }
    static cl_int release(cl_command_queue queue)  //  --  HERE  --
    { return ::clReleaseCommandQueue(queue); }
};
Run Code Online (Sandbox Code Playgroud)

如果您将上面的代码编译为一个简单的可执行文件,它可以正常工作.如果满足以下条件之一,代码也可以工作:

  • CL_DEVICE_TYPE_GPU 被替换为 CL_DEVICE_TYPE_ALL

  • 该行queues.push_back(queue)被删除

那么可能是什么原因以及可能的解决方案是什么?我怀疑它与我的testclass是静态的事实有关,但由于它与可执行文件一起工作,我不知道是什么导致它.

doq*_*tor 4

我过去遇到过类似的问题。

clRetain*功能由 提供支持OpenCL1.2。在您的情况下,当获取第一个 GPU 平台 ( platforms[0].getDevices(...)for CL_DEVICE_TYPE_GPU) 的设备时,它必须恰好是一个平台预OpenCL1.2,因此您会崩溃。当获得任何类型的设备(GPU/CPU/...)时,您的第一个平台将更改为 OpenCL1.2+,一切都很好。

要修复问题集:

#define CL_HPP_MINIMUM_OPENCL_VERSION 110

这将确保clRetain*不会对不受支持的平台进行调用(OpenCL 1.2 之前的版本)


更新:我认为存在一个错误,尽管将最低 OpenCL 版本设置为 1.1,但它仍然尝试在 pre 上cl2.hpp使用clRetain*OpenCL1.2尽管将最低 OpenCL 版本设置为 1.1,但在创建命令队列时将最低 OpenCL 版本设置为 110 和版本过滤对我来说效果很好。

完整的工作示例:

#include "stdafx.h"
#include <vector>

#define CL_HPP_ENABLE_EXCEPTIONS
#define CL_HPP_TARGET_OPENCL_VERSION 200
#define CL_HPP_MINIMUM_OPENCL_VERSION 110
#include <CL/cl2.hpp>

using namespace std;

class TestClass
{
private:
    std::vector<cl::CommandQueue> queues;
    TestClass();

public:
    static const TestClass& getInstance()
    {
        static TestClass instance;
        return instance;
    }
};

TestClass::TestClass()
{
    std::vector<cl::Device> devices;
    vector<cl::Platform> platforms;

    cl::Platform::get(&platforms);

    size_t x = 0;
    for (; x < platforms.size(); ++x)
    {
        cl::Platform &p = platforms[x];
        int v = cl::detail::getPlatformVersion(p());
        short version_major = v >> 16;
        if (version_major >= 2) // OpenCL 2.x
            break;
    }
    if (x == platforms.size())
        return; // no OpenCL 2.0 platform available

    platforms[x].getDevices(CL_DEVICE_TYPE_GPU, &devices); 
    cl::Context context(devices);
    cl::CommandQueue queue(context, devices[0]);

    queues.push_back(queue); 
}

int main()
{
    TestClass::getInstance();
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

更新2

那么造成这种情况的原因是什么以及可能的解决方案是什么?我怀疑这与我的测试类是静态的这一事实有关,但由于它与可执行文件一起工作,我不知道是什么导致了它。

TestClass static 似乎是一个原因。从 python 运行时,释放内存的顺序似乎错误。要解决这个问题,您可能需要添加一个方法,必须在 python 开始释放内存之前显式调用该方法来释放 opencl 对象。

static TestClass& getInstance() // <- const removed
{
    static TestClass instance;
    return instance;
}

void release()
{
    queues.clear();
}

BOOST_PYTHON_MODULE(FrameWork)
{
    TestClass::getInstance();
    TestClass::getInstance().release();
}
Run Code Online (Sandbox Code Playgroud)