Использование структуры в качестве держателя буфера - PullRequest
0 голосов
/ 11 сентября 2018

В моей текущей реализации OpenCL я хотел сэкономить время с аргументами, избегая их передачи каждый раз, когда я хотел использовать буфер внутри ядра, и иметь более короткий список аргументов для своего ядра.

Итак, я создал структуру (рабочую область), которая содержит указатель на буфер в памяти устройства, структура действует как объект с переменной-членом, к которой вы хотите обращаться через время, и вы хотите остаться в живых в течение всего выполнения,У меня никогда не было проблем с AMD GPU или даже с процессором.Но у Nvidia много проблем с этим.Это всегда кажется проблемой выравнивания, никогда не доходящей до правого буфера и т. Д.

Вот некоторый код, чтобы помочь, см. Вопрос ниже:

Структура, определенная на хосте:

 #define SRC_IMG 0       // (float4 buffer) Source image
 #define LAB_IMG 1       // (float4 buffer) LAB image

 // NOTE: The size of this array should be as much as the last define + 1.
 #define __WRKSPC_SIZE__ 2 

 // Structure defined on host.
 struct Workspace
 {
      cl_ulong getPtr[__WRKSPC_SIZE__];
 };

 struct HostWorkspace
 {
      cl::Buffer srcImg;
      cl::Buffer labImg;
 };

Структура, определенная на устройстве:

typedef struct __attribute__(( packed )) gpuWorkspace
{
    ulong getPtr[__WRKSPC_SIZE__]; 
} gpuWorkspace_t; 

Обратите внимание, что на устройстве я использую ulong, а на хосте я использую cl_ulong, как показано здесь OpenCL: использование struct в качестве аргумента ядра .

Поэтому, когда cl :: Buffer для исходного изображения или изображения LAB созданы, я сохраняю их в объект HostWorkspace, поэтому до тех пор, пока этот объект не будет освобожден, ссылка на cl :: Buffer сохраняется, поэтому буфер существует длявесь проект на хосте и defacto на устройстве.

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

__kernel void Workspace_Init(__global gpuWorkspace_t* wrkspc,
                             __global float4* src,
                             __global float4* LAB)
{
    // Get the ulong pointer on the first element of each buffer.
    wrkspc->getPtr[SRC_IMG] = &src[0];
    wrkspc->getPtr[LAB_IMG] = &LAB[0];
}

где wrkspc - это буфер, выделенный с помощью struct Workspace, а src + LAB - это всего лишь буфер, выделяемый как изображения массивов 1D.

И потом, в любом из моих ядер, если я хочу использовать src или LAB,Я делаю следующее:

__kernel void ComputeLABFromSrc(__global gpuWorkspace_t* wrkSpc)
{
    // =============================================================
    // Get pointer from work space.
    // =============================================================

    // Cast back the pointer of first element as a normal buffer you
    // want to use along the execution of the kernel.
    __global float4* srcData = ( __global float4* )( wrkSpc->getPtr[SRC_IMG] );
    __global float4* labData = ( __global float4* )( wrkSpc->getPtr[LAB_IMG] );

    // Code kernel as usual.
}

Когда я начал использовать это, у меня было 4-5 изображений, которые шли хорошо, с другой структурой, подобной этой:

struct Workspace
{
    cl_ulong imgPtr;
    cl_ulong labPtr;
};

, где у каждого изображения был свой указатель.

В определенный момент я достигаю больше изображений, и у меня возникли некоторые проблемы.Так что я ищу в Интернете, и я нашел некоторые рекомендации, что sizeof () структуры может отличаться между устройством / хостом, поэтому я изменяю его на один массив одновременно, и это прекрасно работает до 16 элементов.

Так что я ищу больше, и я нашел рекомендацию об атрибуте ((упакованный)), который я надел на структуру устройства (см. Выше).Но теперь я достигаю 26 элементов, когда я проверяю размер структуры на устройстве или на хосте, размер равен 208 (elements * sizeof (cl_ulong) == 26 * 8).Но у меня все еще есть проблема, похожая на мою предыдущую модель, мой указатель читается где-то в середине предыдущего изображения и т. Д.

Так что мне интересно, если кто-нибудь когда-нибудь попробовал подобную модель (возможно, сдругой подход) или есть какие-либо советы, чтобы иметь «твердую» модель с этим.

Обратите внимание, что все ядра хорошо закодированы, у меня хороший результат при выполнении на AMD или на CPU с тем же кодом.Единственная проблема на Nvidia.

1 Ответ

0 голосов
/ 11 сентября 2018

Не пытайтесь хранить значения указателей на стороне GPU через границы ядра. Им не гарантировано, что они останутся прежними. Всегда используйте индексы. И если ядро ​​использует определенный буфер, вам нужно передать его в качестве аргумента этому ядру.

Ссылки:

  1. Спецификация OpenCL 1.2 (насколько мне известно, nvidia не реализует более новый стандарт) не определяет поведение приведения указателя к целому числу или наоборот.
  2. Раздел 6.9p гласит: "Аргументы функций ядра, которые объявлены как структура или объединение, не позволяют передавать объекты OpenCL как элементы структуры или объединения." Это именно то, что вы пытаются сделать: передача структуры буферов в ядро.
  3. Раздел 6.9a гласит: «Аргументы функций ядра в программе не могут быть объявлены как указатель на указатель (и).» - По сути, это то, что вы пытаетесь подорвать, приведя указатели на целое число и обратно. (пункт 1) Вы не можете «обмануть» OpenCL в этом, будучи четко определенным, минуя систему типов.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...