Позвольте мне объяснить, почему sum[0]
перезаписан, а не обновлен.
В вашем случае из 16 рабочих элементов имеется 16 потоков, которые работают одновременно. Теперь sum[0]
представляет собой единую ячейку памяти, которая совместно используется всеми потоками, а строка sum[0] += input[idx]
запускается каждым из 16 потоков одновременно.
Теперь инструкция sum[0] += input[idx]
(я думаю) расширяет, выполняет чтение sum[0]
, а затем добавляет input[idx]
, прежде чем записывать результат обратно до sum[0]
.
Будет отображаться data race, поскольку несколько потоков считывают и записывают в одно и то же место общей памяти. Так что может случиться:
- Все нити могут считывать значение
sum[0]
перед любым другим потоком записывает свой обновленный результат обратно sum[0]
, в этом случае конечного результат sum[0]
бы значение input[idx]
из нить , которая выполняла самую медленную. Поскольку это будет отличаться каждый раз, , если вы запустите пример несколько раз, вы должны увидеть разные результаты .
- Или, один поток может выполнять несколько медленнее, и в этом случае другой поток может уже написал обновленный результат обратно
sum[0]
перед этой медленной нитью читает sum[0]
, и в этом случае будет прибавление, используя значения более одного потока, но не все темы.
Итак, как вы можете избежать этого?
Вариант 1 - Atomics (хуже вариант):
Вы можете использовать atomics, чтобы заставить все нити, чтобы заблокировать, если другой поток выполняет операцию по общей ячейке памяти, но это, очевидно, приводит к потере так как вы делаете параллельный процесс последовательным (и нести затраты на параллелизацию - например, перемещение памяти между хостом и устройством и создание потоков).
Вариант 2 - уменьшение (лучший вариант):
Лучшим решением было бы уменьшить массив, так как вы можете наиболее эффективно использовать параллелизм, и может дать O производительность (журнал (N)). Вот хороший обзор сокращения с помощью OpenCL: Reduction Example.
Если вы использовали атомы, это сработало бы: 'atomic_add (sum, input [idx])'. Но производительность будет значительно хуже, чем при классическом сокращении, учитывая достаточно большой вектор. Только за 16 рабочих элементов я не уверен, но зачем вообще суммировать 16 элементов с OpenCL? И что именно вы имеете против сокращений? Вы также можете использовать 'work_group_reduce_add' для CL 2.0+. –
Перезаписан, но не обновлен? Что это значит для вас? Кстати, этот вид операции - это то, что нельзя сделать параллельно, что, если сумма [0] считывается одним рабочим элементом непосредственно перед тем, как писать другим? первое изменение просто перейдет в корзину – Guiroux
@HubertApplebaum Спасибо за подсказку! Да, я знаю, что для вектора размера 16, похоже, не стоит использовать OpenCL. Мне было любопытно, будет ли концепция работать. И я ничего не имею против сокращения, просто подумал, что кодирование будет менее и легче читать? – Batko