Мне было интересно, может ли кто-нибудь знать, может ли быть какая-то оптимизация, происходящая с HLSL InterlockedAdd, особенно когда он используется на одном глобальном атомарном счетчике (добавленное значение постоянно во всех потоках) с помощью большое количество нитей.
В некоторой информации, которую я нашел в Интернете, говорится, что атомарные добавления могут создавать серьезные проблемы с конкуренцией: https://developer.nvidia.com/blog/cuda-pro-tip-optimized-filtering-warp-aggregated-atomics/
Конечно, статья выше написана для CUDA (тоже немного устарела, датируется 2014 годом), тогда как меня интересует HLSL InterlockedAdd. С этой целью я написал фиктивный шейдер HLSL для Unity (насколько мне известно, скомпилированный в d3d11 через FXC), где я вызываю InterlockedAdd для одного глобального атомарного счетчика, так что добавленное значение всегда равно одинакова для всех заштрихованных фрагментов. Рассматриваемый фрагмент (запущенный в http://shader-playground.timjones.io/), скомпилированный через FXC, оптимизация 3 уровня, модель затенения 5.0):
**HLSL**:
RWStructuredBuffer<int> counter : register(u1);
void PSMain()
{
InterlockedAdd(counter[0], 1);
}
----
**Assembly**:
ps_5_0
dcl_globalFlags refactoringAllowed
dcl_uav_structured u1, 4
atomic_iadd u1, l(0, 0, 0, 0), l(1)
ret
Затем я немного изменил код и вместо того, чтобы всегда добавлять некоторое постоянное значение, теперь я добавляю значение, которое варьируется в зависимости от фрагмента, поэтому что-то вроде этого:
**HLSL**:
RWStructuredBuffer<int> counter : register(u1);
void PSMain(float4 pixel_pos : SV_Position)
{
InterlockedAdd(counter[0], int(pixel_pos.x));
}
----
**Assmebly**:
ps_5_0
dcl_globalFlags refactoringAllowed
dcl_uav_structured u1, 4
dcl_input_ps_siv linear noperspective v0.x, position
dcl_temps 1
ftoi r0.x, v0.x
atomic_iadd u1, l(0, 0, 0, 0), r0.x
ret
Я реализовал эквиваленты вышеупомянутых фрагментов в Unity и использовал их в качестве фрагментных шейдеров для рендеринга полноэкранного четырехугольника (конечно, семантики вывода нет, но это не имеет значения). Я профилировал получившиеся шейдеры с помощью Nsight Grphics. Достаточно сказать, что разница между двумя вызовами отрисовки была огромной: фрагментный шейдер, основанный на втором фрагменте (InterlockedAdd с переменным значением), работал значительно медленнее.
Я также сделал снимки с помощью RenderDoc, чтобы проверить сборку, и они выглядят идентично тому, что показано выше. Ничто в ассемблерном коде не предполагает такой разительной разницы. И тем не менее, разница есть.
Итак, мой вопрос: происходит ли какая-то оптимизация при использовании HLSL InterlockedAdd на одном глобальном атомарном счетчике, чтобы добавленное значение было постоянным? Возможно ли, что драйвер графического процессора может как-то изменить код?
Характеристики системы:
- NVIDIA Quadro P4000
- Windows 10
- Единство 2019.4