OpenCL изображение гистограммы - PullRequest
6 голосов
/ 03 мая 2011

Я пытаюсь написать ядро ​​гистограммы в OpenCL для вычисления 256 бинарных гистограмм R, G и B входного изображения RGBA32F. Мое ядро ​​выглядит так:

const sampler_t mSampler = CLK_NORMALIZED_COORDS_FALSE |
                           CLK_ADDRESS_CLAMP|
                           CLK_FILTER_NEAREST;


__kernel void computeHistogram(read_only image2d_t input, __global int* rOutput,
                               __global int* gOutput, __global int* bOutput)
{

    int2 coords = {get_global_id(0), get_global_id(1)};

    float4 sample = read_imagef(input, mSampler, coords);

    uchar rbin = floor(sample.x * 255.0f);
    uchar gbin = floor(sample.y * 255.0f);
    uchar bbin = floor(sample.z * 255.0f);

    rOutput[rbin]++;
    gOutput[gbin]++;
    bOutput[bbin]++;


}

Когда я запускаю его на изображении 2100 x 894 (1 877 400 пикселей), я склонен видеть только или около 1 870 000 записанных общих значений, когда суммирую значения гистограммы для каждого канала. Это также другое число каждый раз. Я ожидал этого, поскольку время от времени два ядра, вероятно, получают одно и то же значение из выходного массива и увеличивают его, фактически отменяя одну операцию увеличения (я предполагаю?).

Выход 1 870 000 предназначен для размера рабочей группы {1,1} (что, по-видимому, устанавливается по умолчанию, если я не укажу иное). Если я назначу больший размер рабочей группы, такой как {10,6}, я получу значительно меньшую сумму в моей гистограмме (пропорционально изменению размера рабочей группы). Мне это показалось странным, но я предполагаю, что происходит, когда все рабочие элементы в группе увеличивают значение выходного массива одновременно, и поэтому он просто считается одним приращением?

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

Будучи новичком в OpenCL, я думаю, мне интересно, есть ли более простой способ сделать это (так как кажется, что это должна быть относительно простая проблема GPGPU).

Спасибо!

Ответы [ 4 ]

5 голосов
/ 03 мая 2011

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

Разделите вашу рабочую группу на одномерную для столбцов или строк.Используйте каждое ядро, чтобы суммировать гистограмму для столбца или строки, а затем суммируйте ее глобально с атомарным atom_inc.Это приносит наибольшее количество сумм в личной памяти, что намного быстрее и сокращает количество атомных операций.

Если вы работаете в двух измерениях, вы можете сделать это на отдельных частях изображения.

[РЕДАКТИРОВАТЬ:]

Я думаю, у меня есть лучший ответ: ;-)

Посмотрите: http://developer.download.nvidia.com/compute/opencl/sdk/website/samples.html#oclHistogram

У них есть интересная реализация ...

5 голосов
/ 03 мая 2011

Да, вы пишете в общую память одновременно из множества рабочих элементов, поэтому вы потеряете элементы, если не будете делать обновления безопасным способом (или хуже? Просто не делайте этого) ). Увеличение размера группы фактически увеличивает использование вашего вычислительного устройства, что, в свою очередь, увеличивает вероятность конфликтов. Таким образом, вы теряете больше обновлений.

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

синхронизация + барьер не особенно полезна для вашего случая (и, как вы отметили, в любом случае недоступна для глобальной синхронизации. Причина в том, что 2 группы потоков могут никогда не работать одновременно, поэтому попытка их синхронизации не имеет смысла). Обычно он используется, когда все потоки начинают работать над созданием общего набора данных, а затем все начинают использовать этот набор данных с другим шаблоном доступа.

В вашем случае вы можете использовать атомарные операции (например, atom_inc, см. http://www.cmsoft.com.br/index.php?option=com_content&view=category&layout=blog&id=113&Itemid=168).. Тем не менее, обратите внимание на то, что обновляется очень требовательный адрес памяти (скажем, потому, что тысячи потоков пытаются все записать только в 256 int) ), вероятно, приведет к низкой производительности. Все типичные коды гистограммы, которые проходят через циклы, предназначены для уменьшения конкуренции за данные гистограммы.

2 голосов
/ 31 декабря 2012

Вы можете проверить

1 голос
...