сокращение openCL и передача 2d массива

Вот цикл, который я хочу преобразовать в openCL.

for(n=0; n < LargeNumber; ++n) {    
    for (n2=0; n2< SmallNumber; ++n2) {
        A[n]+=B[n2][n];
    }                                                         
    Re+=A[n];       
}

И вот то, что у меня есть до сих пор, хотя я знаю, что это неправильно и что-то не хватает.

__kernel void openCL_Kernel( __global  int *A,
                         __global  int **B,  
                         __global  int *C, 
                         __global _int64 Re,
                                   int D) 
{

int i=get_global_id(0);
int ii=get_global_id(1);

A[i]+=B[ii][i];

//barrier(..); ?

Re+=A[i];

}

Я полный новичок в подобных вещах. Прежде всего я знаю, что я не могу передать глобальный двойной указатель на ядро ​​openCL. Если можете, подождите несколько дней или около того, прежде чем публиковать решение, я хочу выяснить это для себя, но если вы можете помочь указать мне в правильном направлении, я был бы благодарен.


person MVTC    schedule 06.01.2012    source источник
comment
Я не могу передать глобальный двойной указатель на ядро ​​openCL. Ваш выбор слов меня смутил. Вы можете передать двойной указатель (например, __global double * A). Вы не можете передать 2D-указатель (например, __global int ** B).   -  person vocaro    schedule 06.01.2012
comment
Рассматривали ли вы разделение программы на два отдельных ядра (выполняемых последовательно), одно для внутреннего цикла, а другое для внешнего цикла?   -  person vocaro    schedule 06.01.2012


Ответы (1)


Что касается вашей проблемы с передачей двойных указателей: такая проблема обычно решается путем копирования всей матрицы (или того, над чем вы работаете) в один непрерывный блок памяти, и, если блоки имеют разную длину, передают другой массив, который содержит смещения для отдельные строки (так что ваш доступ будет выглядеть примерно как B[index[ii]+i]).

Теперь для вашего сокращения до Re: поскольку вы не упомянули, на каком устройстве вы работаете, я предполагаю, что это его графический процессор. В этом случае я бы избегал сокращения в том же ядре, поскольку оно будет чертовски медленным в том виде, как вы его разместили (вам придется сериализовать доступ к Re по тысячам потоков (и доступ к A[i] тоже). Вместо этого я бы написал ядро, которое суммирует все B[*][i] в A[i] и помещает сокращение от A до Re в другом ядре и делает это в несколько этапов, то есть вы используете ядро ​​сокращения, которое работает с элементом n и сокращает их до чего-то вроде n / 16 (или любое другое число.) Затем вы итеративно вызываете это ядро ​​до тех пор, пока не дойдете до одного элемента, который и является вашим результатом (я намеренно делаю это описание расплывчатым, поскольку вы сказали, что хотите выяснить, что думает самостоятельно).

В качестве примечания: вы понимаете, что в исходном коде нет хорошего шаблона доступа к памяти? Предполагая, что B относительно большой (и намного больше, чем A из-за второго измерения), если внутренний цикл итерация по внешнему индексу, создаст много кеш-ошибок. Это еще хуже при портировании на графический процессор, который очень чувствителен к последовательному доступу к памяти.

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

for (n2=0; n2< SmallNumber; ++n2)
  for(n=0; n < LargeNumber; ++n)    
    A[n]+=B[n2][n];
for(n=0; n < LargeNumber; ++n)                                                 
  Re+=A[n];       

Это особенно верно, если у вас есть компилятор, который хорошо справляется с автовекторизацией, поскольку он может векторизовать эту конструкцию, но очень маловероятно, что это удастся сделать для исходного кода (и если он не может доказать, что A и B[n2] не может ссылаться на ту же самую память, он не может превратить в нее исходный код).

person Grizzly    schedule 07.01.2012
comment
Спасибо! Это дает мне много поводов для размышлений. - person MVTC; 07.01.2012