Что делает mem_fence() в OpenCL, в отличие от барьера ()?

В отличие от barrier() (что, я думаю, я понимаю), mem_fence() не влияет на все элементы в рабочей группе. Спецификация OpenCL говорит (раздел 6.11.10), для mem_fence():

Заказывает загрузки и сохраняет рабочий элемент, выполняющий ядро.

(так что это относится к одному рабочему элементу).

Но в то же время в разделе 3.3.1 говорится, что:

В памяти рабочего элемента есть согласованность загрузки / хранения.

поэтому внутри рабочего элемента память постоянна.

Так что же это за вещь mem_fence() полезный для? Он не работает с элементами, но не нужен для элемента...

Обратите внимание, что я не использовал атомарные операции (раздел 9.5 и т. Д.). Является ли идея, что mem_fence() используется в сочетании с тем? Если это так, я хотел бы увидеть пример.

Благодарю.

Спецификация, для справки.

Обновление: я вижу, как это полезно при использовании с barrier() (неявно, так как барьер вызывает mem_fence()) - но наверняка их должно быть больше, так как он существует отдельно?

3 ответа

Решение

Чтобы попытаться выразить это более четко (надеюсь),

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

Это происходит от: http://developer.download.nvidia.com/presentations/2009/SIGGRAPH/asia/3_OpenCL_Programming.pdf

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

Я признаю, что это все еще сбивает с толку, и я не буду клясться, что мое понимание на 100% верно, но я думаю, что это по крайней мере общая идея.

Следовать за:

Я нашел эту ссылку, которая говорит о заборах памяти CUDA, но та же самая общая идея применима к OpenCL:

http://developer.download.nvidia.com/compute/cuda/2_3/toolkit/docs/NVIDIA_CUDA_Programming_Guide_2.3.pdf

Ознакомьтесь с разделом B.5 Функции забора памяти.

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

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

После этого, если остается больше работы, рабочая группа, которая увеличила счетчик до значения ("размер рабочей группы" - 1), считается последней рабочей группой. Эта рабочая группа продолжает заканчивать.

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

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

Надеюсь, в этом есть какой-то смысл. Это сбивает с толку.

Вот как я это понимаю (я все еще пытаюсь это проверить)

memory_fence только удостоверится, что память является согласованной и видимой для всех потоков в группе, т.е. выполнение НЕ останавливается, пока не произойдет другая транзакция памяти (локальная или глобальная). Это означает, что если есть инструкция перемещения или инструкция добавления после memory_fenceустройство продолжит выполнение этих инструкций "транзакции без памяти".

barrier с другой стороны остановит исполнение, точка. И будет продолжаться только после того, как все потоки достигнут этой точки И все операции с памятью будут очищены.

Другими словами, barrier это надмножество mem_fence, barrier может оказаться дороже с точки зрения производительности, чем mem_fence,

Забор гарантирует, что грузы и / или склады, выпущенные до забора, будут завершены до каких-либо грузов и / или складов, выпущенных после забора. Никакой синк не подразумевается одними заборами. Операция барьера поддерживает ограничение чтения / записи в одной или обеих областях памяти, а также блокировку до тех пор, пока все рабочие элементы в рабочей группе дающего не достигнут этого.

Другие вопросы по тегам