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

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

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

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

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

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

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

Для чего полезно mem_fence()? Он не работает во всех элементах, но не нужен внутри элемента...

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

Спасибо.

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

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

Ответ 1

Чтобы сделать это более четко (надеюсь),

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

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

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

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

Follow Up:

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

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

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

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

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

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

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

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

Надеюсь, это имеет смысл. Это запутанно.

Ответ 2

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

Ответ 3

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

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

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

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