2013-04-28 4 views
4

У меня есть следующая функция ядра opencl, чтобы получить сумму столбца изображения.ускорение при использовании float4, opencl

__kernel void columnSum(__global float* src,__global float* dst,int srcCols, 
          int srcRows,int srcStep,int dstStep) 
{ 

    const int x = get_global_id(0); 
    srcStep >>= 2; 
    dstStep >>= 2; 

    if (x < srcCols) 
    { 
     int srcIdx = x ; 
     int dstIdx = x ; 

     float sum = 0; 

     for (int y = 0; y < srcRows; ++y) 
     { 
      sum += src[srcIdx]; 
      dst[dstIdx] = sum; 
      srcIdx += srcStep; 
      dstIdx += dstStep; 
     } 
    } 
} 

поручает, что каждый процесс нити столбца здесь, так что много нитей может получить column_sum каждой колонки параллельно.

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

__kernel void columnSum(__global float* src,__global float* dst,int srcCols, 
          int srcRows,int srcStep,int dstStep) 
{ 

    const int x = get_global_id(0); 

    srcStep >>= 2; 
    dstStep >>= 2; 
    if (x < srcCols/4) 
    { 
     int srcIdx = x ; 
     int dstIdx = x ; 

     float4 sum = (float4)(0.0f, 0.0f, 0.0f, 0.0f); 

     for (int y = 0; y < srcRows; ++y) 
     { 
      float4 temp2; 
      temp2 = vload4(0, &src[4 * srcIdx]); 
      sum = sum + temp2; 

      vstore4(sum, 0, &dst[4 * dstIdx]); 

      srcIdx += (srcStep/4); 
      dstIdx += (dstStep/4); 
     } 
    } 
} 

В этом случае, теоретически, я считаю, что время, затрачиваемое на втором ядре, чтобы обработать изображение должно быть 1/4 от времени, потребляемого первой функцией ядра. Однако, независимо от того, насколько велико изображение, два ядра почти потребляют одно и то же время. Я не знаю почему. Можете ли вы, ребята, дать мне несколько идей? T

+0

Тогда узким местом является не часть обмена информацией. Является ли производительность приемлемой, кроме того, что вы не получаете 1/4 ускорения? Я прошу, чтобы у вас была проблема с синхронизацией. Сколько вычислений вы выполняете? Простое суммирование - всего лишь пример? –

+0

Вы знаете о инструкциях SIMD, не так ли? Они могут выполнять одну и ту же операцию по значениям 2, 4, 8 или 16 (или больше, в зависимости от аппаратных) одновременно. Возможно, поэтому время одинаковое, потому что он эффективно выполняет ту же работу, но использует инструкции SIMD. Это очень распространено в графических процессорах, но также может наблюдаться на процессорах в меньшей степени. 4 поплавка хороша для регистра SSE, звучит правильно. Это, и вы можете просто быть узким местом в передаче памяти, как отмечает Csaba. – Thomas

+0

Ну, причина, по которой я использую float4, выглядит так: если у нас есть 256 потоков, которые могут работать одновременно. У нас есть изображение 1024 * 1024. Если мы используем первое ядро, 256 потоков сначала обрабатывают первые 256 столбцов, затем обрабатывают следующие 256 столбцов и так далее, пока весь столбец не будет завершен. В этом случае будет потребляться 4 * t, где t обозначает время, затрачиваемое 256 потоками для обработки 256 столбцов. Однако, если мы используем второе ядро, поскольку каждый поток обрабатывает 4 элемента в строке изображения, 256 потоков могут обрабатывать 1024 столбца с временем t.Это просто оценка. @Thomas – user2326258

ответ

0

Я думаю, что на GPU float4 не является SIMD-операцией в OpenCL. Другими словами, если вы добавите два значения float4, сумма будет выполнена в четыре шага, а не сразу. Floatn действительно предназначен для процессора. На графическом процессоре floatn служит лишь удобным синтаксисом, по крайней мере, на картах Nvidia. Каждый поток на GPU действует так, как если бы он был скалярным процессором без SIMD. Но потоки в warp не являются независимыми, как в CPU. Правильный способ подумать о моделях GPGPU - это однопользовательские несколько потоков (SIMT). http://www.yosefk.com/blog/simd-simt-smt-parallelism-in-nvidia-gpus.html

Вы пытались запустить свой код на CPU? Я думаю, что код с float4 должен работать быстрее (потенциально в четыре раза быстрее), чем скалярный код на CPU. Также, если у вас есть процессор с AVX, тогда вы должны попробовать float8. Если код float4 работает быстрее на CPU, а float8 должен быть еще быстрее на процессоре с AVX.

+0

Это правда под графическими процессорами nVidia, которые следуют сверхскалярной модели, однако AMD GPU имеют чрезвычайно широкие регистры SIMD (8 или 16, последний раз я проверил). – Thomas

+0

Это может быть правдой, я не знаю графических процессоров AMD, но, похоже, это не соответствует модели GPGPU. Деформация - это действительно одна огромная операция SIMD, поэтому она использует SIMD, она просто скрыта. Если у AMD есть SIMD-регистры для каждого потока, почему бы им просто не иметь 8 или 16 потоков на регистр (что делает Nvidia) вместо одного потока и 8/16 SIMD-регистра? – 2013-04-28 08:37:47

+0

У него есть, iirc, warp («wavefront», в AMD-говорить) архитектура немного отличается, но они все еще там. AMD GPU не имеет «одного потока»: p Я не знаю их мотивации, но именно поэтому AMD GPU обычно предпочитают неловко параллельные проблемы, они просто имеют более высокую вычислительную мощность для эквивалентного оборудования. У графических процессоров Nvidia есть другие преимущества. – Thomas

2

какой архитектура вы используете?

Использование Float4 имеет более высокий параллелизм на уровне команд (и затем требуют 4 раза меньше потоков) так что теоретически должно быть быстрее (см http://www.cs.berkeley.edu/~volkov/volkov10-GTC.pdf)

Однако сделал я правильно вас понимаю ядро ​​вы делаете префиксов сумму (вам магазин частичная сумма на каждой итерации у)? Если это так, из-за магазинов узкое место находится в памяти.

+0

Это очень интересная ссылка. Я удивлен, что float4 имеет значение. Я не думал об ИЛП. Без ИЛП я не думаю, что это было бы важно.В четыре раза меньше потоков, но в четыре раза больше расчётов на поток. – 2013-04-28 08:49:17

4

Вы непосредственно читаете значения из массива «src» (глобальная память). Как правило, это в 400 раз медленнее частной памяти. Ваше узкое место определенно зависит от доступа к памяти, а не от операции «добавить».

Когда вы переходите из float в float4, операция вектора (добавление/умножение/...) более эффективна благодаря способности графического процессора работать с векторами. Однако чтение/запись в глобальную память остается неизменным. И поскольку это основное узкое место, вы не увидите никакого ускорения.

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

6

Форматы векторных данных OpenCL, такие как float4, лучше подходят для старых архитектур GPU, особенно для графических процессоров AMD. Современные графические процессоры не имеют регистров SIMD, доступных для отдельных рабочих элементов, в этом отношении они являются скалярными. CL_DEVICE_PREFERRED_VECTOR_WIDTH_* равен 1 для драйвера OpenCL на графических процессорах NVIDIA Kepler и интегрированной графике Intel HD. Поэтому добавление векторов float4 на современный графический процессор должно потребовать 4 операции. С другой стороны, драйвер OpenCL на процессоре Intel Core имеет CL_DEVICE_PREFERRED_VECTOR_WIDTH_FLOAT, равный 4, поэтому эти векторы могут быть добавлены за один шаг.

0

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

__ ядро ​​аннулируются __ атрибут __ ((vec_type_hint (целое)))

или

__ __ ядро ​​недействительным атрибут __ ((vec_type_hint (int4)))

или некоторые floatN, как вы хотите

чтения мор e: https://www.khronos.org/registry/cl/sdk/1.0/docs/man/xhtml/functionQualifiers.html

Смежные вопросы