我想知道是否有人知道HLSL InterlockedAdd是否会进行某种优化,特别是当大量线程在单个全局原子计数器上使用它(所有线程的附加值都是常数)时。
我在网络上发现的一些信息表明,原子添加会造成重大的争用问题:https://developer.nvidia.com/blog/cuda-pro-tip-optimized-filtering-warp-aggregated-atomics/
当然,上面这篇文章是为CUDA写的(也是在2014年的时候写的),而我对HLSL InterlockedAdd很感兴趣。为了达到这个目的,我编写了一个虚拟的HLSL着色器,用于统一(据我所知,通过FXC编译成d3d11 ),其中我在一个全局原子计数器上调用InterlockedAdd,这样所有阴影片段的附加值都是相同的。问题中的片段(运行在http://shader-playground.timjones.io/中,通过FXC编译,优化lvl 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
我在联合中实现了上述代码片段的等价物,并使用它们作为我的片段着色器来呈现全屏四角体(当然,没有输出语义,但这是不相关的)。我用Nsight Grphics描述了最终的着色器。可以说,两个抽签调用之间的差异是巨大的,基于第二个片段的片段着色器(具有可变值的InterlockedAdd)要慢得多。
我还使用RenderDoc进行捕获,以检查程序集,它们看起来与上面显示的相同。装配代码中没有任何东西显示出如此巨大的差异。然而,差别就在这里。
因此,我的问题是:在单个全局原子计数器上使用HLSL InterlockedAdd时,是否存在某种优化,以至于附加值是一个常数?也许,GPU驱动程序可以以某种方式重新排列代码吗?
系统规格:
P4000
发布于 2021-03-24 15:37:38
GPU上的像素着色器运行simd组中的像素,称为波前。如果当前执行的代码不会根据正在呈现的像素更改,则整个组只需运行一次代码。如果它基于像素发生变化,那么每个像素都需要运行唯一的代码。
在第一个版本中,64像素的波前将作为单个simd InterlockedAdd<64>执行代码(计数器,1);甚至可能将其优化为InterlockedAdd(计数器,64);在第二个示例中,它变成了一系列串行的、非simd的代码,并且变得昂贵了64倍。
这是一个过度简化,还有其他的技巧,GPU用于共享计算资源。但是,一个好的经验法则是使尽可能多的代码可以通过每一个附近的像素共享。
https://stackoverflow.com/questions/65233108
复制相似问题