Это с семантикой Вулкана, если это имеет значение.
Предположим следующее:
layout(...) coherent buffer B
{
uint field;
} b;
Скажем, поле модифицируется другими вызовами того же шейдера (или производного шейдера) через функции atomic*()
.
Если вызов шейдера хочет выполнить атомарное чтение из этого field
(с той же семантикой, что и atomicCounter()
в GLES, если бы вместо этого был atomic_uint
), есть ли какая-либо разница между следующими двумя (кроме очевидного того, что один из них выполняет запись как и читать)?
uint read_value = b.field;
uint read_value2 = atomicAdd(b.field, 0);
Он эмулирует атомарные счетчики, поэтому coherent
с другими atomicAdd
операциями над тем же uint
(которые эмулируют atomicCounterIncrement
и atomicCounterDecrement
).
Другие atomicAdd
операции куда? На той же стадии шейдера? На стадиях производных шейдеров? На этапах шейдера от последующих команд рендеринга? Утверждение, что вы «эмулируете атомарные счетчики», ничего не говорит о реальных операциях, которые вы выполняете.
Очевидно, что последующие команды рендеринга нуждаются в других барьерах. Я спрашивал о других вызовах того же шейдера (как указано в одном из вопросов). Я не думал о производных шейдерных стадиях, мне было бы любопытно узнать, имеет ли это значение (может быть, на мозаичном графическом процессоре?). --- Извините, я не был более конкретным, я предположил, что совершенно очевидно, как atomic_uint
будет эмулироваться с помощью buffer AC { uint counters[]; } ac;
И я хочу сказать, что то, как вы это имитируете, зависит от вашего варианта использования. Здесь нет универсального решения.
Я чувствую, что мои детализированные вопросы на самом деле совершенно не зависят от имитации атомарных счетчиков. Например, я спрашиваю, если буфер coherent
и вы читаете из него, а также atomic*()
из него, может ли прочитанное значение кэшироваться более локально, чем результат атомарных операций. Ответ «да/нет» на этот (и на другой вопрос) даст мне достаточно информации, чтобы знать, что мне нужно знать для эмуляции.
Позвольте мне перефразировать вопрос, чтобы не упоминать атомные счетчики.
И что означает «кэшируется более локально» в отношении фактического поведения?
Скажем, если результат atomicAdd(field)
(записанное значение) хранится в L2, а чтение field
приводит к его кэшированию в L1 (что делает будущие модификации другими вызовами невидимыми), это будет более локальным (что, по-видимому, не является правильную терминологию).
Я хочу сказать, что это вопрос конкретной реализации, а не поведение. Значение coherent
определяется с точки зрения поведения, отношения между чтением и записью из и в различные места. Реализации должны проработать детали кешей, чтобы реализовать эту реализацию. И эти детали будут специфичны для реализации, а не для чего-то полезного в целом.
Правильно, я хотел спросить о поведении coherent
, но думал о том, как это реализовано. Итак, теперь вопрос понятен?
Чтобы напрямую ответить на вопрос, эти две строки кода генерируют разные инструкции с разными характеристиками производительности и использованием аппаратного конвейера.
uint read_value = b.field; // generates a load instruction
uint read_value2 = atomicAdd(b.field, 0); // generates an atomic instruction
buffer_load_dword
против buffer_atomic_add
LDG
против ATOM
В разделе 4.10 квалификаторов памяти GLSL-спецификация отмечается, что coherent
касается только видимости операций чтения и записи между вызовами (потоками шейдера). Они также оставили комментарий о подразумеваемой производительности:
When accessing memory using variables not declared as coherent, the memory accessed by a shader may be cached by the implementation to service future accesses to the same address. Memory stores may be cached in such a way that the values written might not be visible to other shader invocations accessing the same memory. The implementation may cache the values fetched by memory reads and return the same values to any shader invocation accessing the same memory, even if the underlying memory has been modified since the first memory read. While variables not declared as coherent might not be useful for communicating between shader invocations, using non-coherent accesses may result in higher performance.
Точкой когерентности в системах памяти GPU обычно является кеш последнего уровня (кэш L2), что означает, что все когерентные обращения должны выполняться кешем L2. Это также означает, что когерентные буферы нельзя кэшировать в L1 или других кэшах, расположенных ближе к шейдерным процессорам. Современные графические процессоры также имеют выделенное атомарное оборудование в кэшах L2; обычная нагрузка не будет использовать их, но atomicAdd(..., 0)
пройдет через них. Атомное оборудование обычно имеет меньшую пропускную способность, чем полный кэш L2.
Спасибо, что подтвердили мои подозрения. Я понимаю, что они генерируют разные сборки. Мой вопрос был действительно о том, могут ли они потенциально «вести себя» по-другому. Другими словами, гарантируется ли, что простое чтение переменной всегда возвращает то же значение, что и atomicAdd(var, 0)
? Я все больше убеждаюсь, что это так.
Кстати, я очень хорошо знаком с GCN, и я могу сказать, что в этой архитектуре они будут эквивалентны (с некоторыми предположениями о драйверах). То, что я ищу, - это спецификация, гарантирующая, что так будет всегда. Я предполагаю, что ваша цитата косвенно говорит об этом: «При доступе к памяти с использованием переменных, не объявленных как когерентные, память, к которой обращается шейдер, может кэшироваться реализацией для обслуживания будущих обращений к тому же адресу». Итак, (!coherent => cached), хотя математически мы не можем вывести это (coherent => !cached), но я бы больше связал это с плохой формулировкой.
SPIR-V имеет инструкцию OpAtomicLoad
. Предположительно, существует по крайней мере одно устройство, в котором неатомарная загрузка не может заменить атомарную загрузку, независимо от того, какой квалификатор имеет дескриптор буфера.
К сожалению, мне известно, что не существует конструкции Vulkan GLSL, которая может переводиться в OpAtomicLoad
.
Этот вопрос требует более подробной информации. Например, что означает «достаточно»? «Достаточно» для выполнения какой операции?
coherent
имеет значение относительно пары операций. С какой операцией вы хотите сделать атомарный доступcoherent
? Какая связь между этими двумя операциями? И так далее. Вопрос, как указано, просто не содержит достаточной информации.