Базовый адрес объекта памяти OpenCL

Я хочу пройти дерево на GPU с помощью OpenCL, поэтому я собираю дерево в непрерывный блок на хосте и меняю адреса всех указателей, чтобы они были согласованы на устройстве следующим образом:

TreeAddressDevice = (size_t) BaseAddressDevice + ((size_t) TreeAddressHost - (size_t) BaseAddressHost);

Мне нужен базовый адрес буфера памяти: на хосте я выделяю память для буфера следующим образом: cl_mem tree_d = clCreateBuffer (...);

Проблема в том, что cl_mems - это объекты, отслеживающие внутреннее представление данных. Технически это указатели на объект, но не указатели на данные. Единственный способ получить доступ к cl_mem из ядра - передать его в качестве аргумента через setKernelArgs.

Здесь http://www.proxya.net/browse.php?u=%3A%2F%2Fwww.khronos.org%2Fmessage_boards%2Fviewtopic.php%3Ff%3D37%26amp%3Bt%3D2900&b=28 я нашел следующее решение, но оно не работает:

__kernel void getPtr( __global void *ptr, __global void *out )

    {
    *out = ptr;
    }

который может быть вызван следующим образом

Код:

...

    cl_mem auxBuf = clCreateBuffer( context, CL_MEM_READ_WRITE, sizeof(void*), NULL, NULL );
    void *gpuPtr;

    clSetKernelArg( getterKernel, 0, sizeof(cl_mem), &myBuf );
    clSetKernelArg( getterKernel, 1, sizeof(cl_mem), &auxBuf );
    clEnqueueTask( commandQueue, getterKernel, 0, NULL, NULL );
    clEnqueueReadBuffer( commandQueue, auxBuf, CL_TRUE, 0, sizeof(void*), &gpuPtr, 0, NULL, NULL );

    clReleaseMemObject(auxBuf);

...

Теперь "gpuPtr" должен содержать адрес начала "myBuf" в памяти графического процессора.

Решение очевидное и я не могу его найти? Как мне вернуть указатель на память устройства при создании буферов?


person zoe vas    schedule 15.01.2013    source источник


Ответы (2)


Это потому, что в модели OpenCL память хоста и память устройства не пересекаются. Указатель в памяти устройства не будет иметь значения на хосте.

Вы можете сопоставить буфер устройства с памятью хоста с помощью clEnqueueMapBuffer. Сопоставление синхронизирует устройство с хостом, а отключение синхронизирует обратно хост с устройством.

Обновить. Как вы объясняете в комментариях, вы хотите отправить древовидную структуру на графический процессор. Одним из решений было бы хранить все узлы дерева внутри массива, заменяя указатели на узлы индексами в массиве.

person Eric Bainville    schedule 15.01.2013
comment
Спасибо за ответ ...: D С флагом CL_MEM_ALLOC_HOST_PTR приложение хочет, чтобы реализация OpenCL выделяла память из доступной для хоста памяти, это похоже на закрепленную память в CUDA. Но я не хочу этого. Мне нужно скопировать все дерево на устройство, но я должен изменить адреса в host: (size_t) базовый адрес объекта памяти на устройстве + ((size_t) Ptr - (size_t) HostPtr). По этой причине мне нужен базовый адрес объекта памяти на устройстве. Там в любом случае? - person zoe vas; 15.01.2013
comment
@zoevas Нет, ты делаешь это неправильно. Ваше дерево не должно заботиться о базовом адресе и должно использовать смещения (или индексы, как предложил Эрик). В каждом узле на хосте вычислите (nodeAddress - baseAddress) / nodeSize и это ваше смещение. На устройстве получите доступ к узлу с помощью nodeList [nodeOffset]. - person Thomas; 02.03.2013

Как указал Эрик, необходимо учитывать два набора памяти: память хоста и память устройства. По сути, OpenCL пытается скрыть мельчайшие детали этого взаимодействия, вводя объект-буфер, с которым мы можем взаимодействовать в нашей программе на стороне хоста. Как вы отметили, проблема с этой методологией заключается в том, что она скрывает детали нашего устройства, когда мы хотим сделать что-то более сложное, чем разработчики OpenCL предполагали или допускали в своей области. Решение здесь - помнить, что ядра OpenCL используют C99 и что этот язык позволяет нам без проблем обращаться к указателям. Имея это в виду, мы можем просто потребовать, чтобы указатель хранился в беззнаковой целочисленной переменной, на которую позже можно будет ссылаться.

Ваша реализация была на правильном пути, но для завершения передачи требовалось немного больше синтаксиса C.

Ядро OpenCL:

// Kernel used to obtain pointer from target buffer
__kernel void mem_ptr(__global char * buffer, __global ulong * ptr)
{
    ptr[0] = &buffer[0];
}

// Kernel to demonstrate how to use that pointer again after we extract it.
__kernel void use_ptr(__global ulong * ptr)
{
    char * print_me = (char *)ptr[0];
    /* Code that uses all of our hard work */
    /* ... */
}

Хост-программа:

// Create the buffer that we want the device pointer from (target_buffer) 
//  and a place to store it (ptr_buffer).
cl_mem target_buffer = clCreateBuffer(context, CL_MEM_READ_WRITE, 
                                      MEM_SIZE * sizeof(char), NULL, &ret);
cl_mem ptr_buffer    = clCreateBuffer(context, CL_MEM_READ_WRITE,
                                      1 * sizeof(cl_ulong), NULL, &ret);

/* Setup the rest of our OpenCL program */    
/* .... */

// Setup our kernel arguments from the host...
ret = clSetKernelArg(kernel_mem_ptr, 0, sizeof(cl_mem), (void *)&target_buffer);
ret = clSetKernelArg(kernel_mem_ptr, 1, sizeof(cl_mem), (void *)&ptr_buffer);
ret = clEnqueueTask(command_queue, kernel_mem_ptr, 0, NULL, NULL);

// Now it's just a matter of storing the pointer where we want to use it for later.
ret = clEnqueueCopyBuffer(command_queue, ptr_buffer, dst_buffer, 0, 1 * sizeof(cl_ulong),
                          sizeof(cl_ulong), 0, NULL, NULL);
ret = clEnqueueReadBuffer(command_queue, ptr_buffer, CL_TRUE, 0,
                          1 * sizeof(cl_ulong), buffer_ptrs, 0, NULL, NULL);  

Вот и все. Теперь имейте в виду, что вам не обязательно использовать переменные типа char, которые я использовал; он работает для любого типа. Однако я бы рекомендовал использовать cl_ulong для хранения указателей. Это не имеет значения для устройств с менее чем 4 ГБ доступной памяти. Но для устройств с большим адресным пространством вам нужно использовать cl_ulong. Если вам абсолютно НЕОБХОДИМО сэкономить место на вашем устройстве, но у вас есть устройство с памятью> 4 ГБ, тогда вы можете создать структуру, которая может хранить нижние 32 LSB адреса в типе uint, при этом MSB хранится в мелкий тип.

person Gorethox    schedule 15.09.2017
comment
Спасибо @gorethox. Создание целого ядра и буфера только для целей запроса указателя устройства звучит действительно неуместно. (Я виню OpenCL, а не вас.) Неужели нет эквивалента cudaHostGetDevicePointer? - person All the Rage; 05.08.2020
comment
Привет, @AlltheRage! Я давно не работал с OpenCL, поэтому вполне возможно, что они осознали необходимость в этом и реализовали функцию для поддержки этого. Однако в то время, когда я писал это, я вообще не мог найти эквивалента. Большинство помощников, которые я нашел, на самом деле не думали, что что-то подобное было необходимо. - person Gorethox; 23.11.2020