Частичная инкапсуляция OpenCL, вызывающая ошибку сегментации, включая пример кода
Это полностью сбивает меня с толку. Два набора кода, которые должны быть логически одинаковыми, один падает только на графическом процессоре, в то время как оба нормально работают на процессоре. Вот тестовый код:
#include <iostream>
#include <CL/cl.hpp>
class Device
{
public:
cl::Platform platform_;
cl::Device device_;
cl::Context context_;
cl::CommandQueue queue_;
Device( void ) : platform_()
, device_()
, context_()
, queue_() {}
Device(int32_t platform, int32_t device) : platform_()
, device_()
, context_()
, queue_()
{
std::vector<cl::Platform> platforms;
cl::Platform::get(&platforms);
platform_ = platforms[platform];
std::vector<cl::Device> devices;
platform_.getDevices(CL_DEVICE_TYPE_GPU, &devices);
device_ = devices[device];
cl_context_properties properties[3] = {
CL_CONTEXT_PLATFORM,
(cl_context_properties)(platform_)(),
0
};
cl_int clErr = CL_SUCCESS;
context_ = cl::Context(device_, properties, NULL, NULL, &clErr);
queue_ = cl::CommandQueue(context_,device_,0,&clErr);
}
};
int main()
{
Device device(0,0);
cl::Program::Sources source;
std::string src =
"__kernel void Pointless(uint total, __global uint *data)"\
"{"\
" uint perStream=total/get_global_size(0);"\
" __global uint *dest=data+get_global_id(0)*perStream;"\
" for(uint i=0;i<perStream;i++)"\
" dest[i] = 1;"\
"}";
source.push_back({src.c_str(),src.length()});
cl_int clErr = CL_SUCCESS;
cl::Program program = cl::Program(device.context_,source,&clErr);
if (clErr != CL_SUCCESS)
{
std::cerr << "Failed to create program: " << clErr << std::endl;
return 1;
}
clErr = program.build({device.device_});
if(clErr != CL_SUCCESS)
{
std::cerr << "Failed to build program: " << clErr << std::endl;
std::cerr << program.getBuildInfo<CL_PROGRAM_BUILD_LOG>(device.device_) << std::endl;
return 1;
}
uint32_t samples = 16*256;
cl::make_kernel<cl_uint,cl::Buffer> Pointless(cl::Kernel(program,"Pointless"));
cl::Buffer device_samples(device.context_,CL_MEM_READ_WRITE,sizeof(cl_uint)*samples);
Pointless(cl::EnqueueArgs(device.queue_, cl::NDRange(16)), samples, device_samples).wait();
std::vector<cl_uint> host_samples(samples);
device.queue_.enqueueReadBuffer(device_samples,CL_TRUE,0,sizeof(cl_uint)*samples,host_samples.data());
for (auto x: host_samples)
std::cout << x;
std::cout << std::endl;
return 0;
}
Кажется, вышеописанное не помогает: я получаю ошибку сегментации enqueueReadBuffer
, Еще интереснее то, что он работает только на графическом процессоре (Intel P4000). Процессор (i3 3xxx) запускает его без проблем (изменение CL_DEVICE_TYPE_GPU
в CL_DEVICE_TYPE_CPU
проверить на процессоре).
Теперь приведенный ниже код отлично работает с обоими типами устройств.
#include <iostream>
#include <CL/cl.hpp>
int main()
{
std::vector<cl::Platform> platforms;
cl::Platform::get(&platforms);
cl::Platform platform = platforms[0];
std::vector<cl::Device> devices;
platform.getDevices(CL_DEVICE_TYPE_GPU, &devices);
cl::Device device = devices[0];
cl_context_properties properties[3] = {
CL_CONTEXT_PLATFORM,
(cl_context_properties)(platform)(),
0
};
cl_int clErr = CL_SUCCESS;
cl::Context context(device, properties, NULL, NULL, &clErr);
cl::CommandQueue queue(context,device,0,&clErr);
cl::Program::Sources source;
std::string src =
"__kernel void Pointless(uint total, __global uint *data)"\
"{"\
" uint perStream=total/get_global_size(0);"\
" __global uint *dest=data+get_global_id(0)*perStream;"\
" for(uint i=0;i<perStream;i++)"\
" dest[i] = 1;"\
"}";
source.push_back({src.c_str(),src.length()});
cl::Program program = cl::Program(context,source,&clErr);
clErr = program.build({device});
if(clErr != CL_SUCCESS)
{
std::cerr << program.getBuildInfo<CL_PROGRAM_BUILD_LOG>(device) << std::endl;
}
uint32_t samples = 16*256;
cl::make_kernel<cl_uint,cl::Buffer> Pointless(cl::Kernel(program,"Pointless"));
cl::Buffer device_samples(context,CL_MEM_READ_WRITE,sizeof(cl_uint)*samples);
Pointless(cl::EnqueueArgs(queue, cl::NDRange(16)), samples, device_samples).wait();
std::vector<cl_uint> host_samples(samples);
queue.enqueueReadBuffer(device_samples,CL_TRUE,0,sizeof(cl_uint)*samples,host_samples.data());
for (auto x: host_samples)
std::cout << x;
std::cout << std::endl;
return 0;
}
Очевидно, я упускаю что-то очень простое здесь. Они оба используют Intel ICD (у меня нет устройства AMD в этой системе).
2 ответа
(Только начал писать, поэтому я пока не могу комментировать)
Я проверил ваш код с помощью реализации Nvidia (используется через Intel ICD). Компилятор C++ был G++ 4.7.3. Оба ваших примера отлично работали на GPU, а также на доступном процессоре Intel.
Таким образом, проблема почти наверняка ограничена только при внедрении Intel GPU.
Так что я, возможно, наткнулся на ошибку в cl.hpp
обертка. cl::Context context_; context_ = cl::Context(...);
неправильно назначает адрес базовых объектов. И то и другое cl::Context context_ = cl::Context(...)
а также cl::Context context_(...);
работать хорошо, однако.
Я тестировал в G++ 4.8.1 и MSVC 2010 с одинаковыми результатами в обоих. То, что он работает нормально с процессором, указывает на ошибку в Intel ICD, возможно. Хотя документация группы Хронос о cl::Context
заявляет, что он "неявно сохраняет" с кратким комментарием о том, что приложение может освобождать базовый объект без уведомления обертки.
Спасибо Шарпнели, ДаркЗеро.