如果 `buffer` 是 `coherent`,那么读取字段和执行 `atomicAdd(field, 0)` 之间有什么区别吗?
If a `buffer` is `coherent`, is there any difference between reading a field or doing `atomicAdd(field, 0)`?
这与 Vulkan 语义有关,如果有任何区别的话。
假设如下:
layout(...) coherent buffer B
{
uint field;
} b;
假设该字段正在被同一着色器(或派生着色器)的其他调用通过 atomic*()
函数修改。
如果着色器调用想要从这个 field
执行原子读取(与 GLES 中的 atomicCounter()
具有相同的语义,如果这是一个 atomic_uint
),是否存在以下两者之间有什么区别(除了显然其中一个既能写又能读)?
uint read_value = b.field;
uint read_value2 = atomicAdd(b.field, 0);
直接回答问题,这两行代码生成不同的指令,具有不同的性能特征和硬件流水线使用。
uint read_value = b.field; // generates a load instruction
uint read_value2 = atomicAdd(b.field, 0); // generates an atomic instruction
- AMD disassembly can be seen in this online Shader Playground --
buffer_load_dword
对比 buffer_atomic_add
- Dissecting the NVIDIA Volta GPU Architecture via Microbenchmarking --
LDG
对比 ATOM
GLSL spec 部分 4.10 内存限定符指出 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内存系统中的point-of-coherence通常是last-level缓存(L2缓存),意味着所有相关访问都必须由L2缓存执行。这也意味着连贯缓冲区不能缓存在 L1 或更靠近着色器处理器的其他缓存中。现代 GPU 在二级缓存中也有专用的原子硬件;普通负载不会使用这些,但 atomicAdd(..., 0)
会通过这些。原子硬件的带宽通常低于完整的二级缓存。
SPIR-V 有一个 OpAtomicLoad
指令。据推测,无论缓冲区描述符具有什么限定符,至少有一种硬件非原子加载无法替代原子加载。
不幸的是,据我所知,没有可以转换为 OpAtomicLoad
的 Vulkan GLSL 结构。
这与 Vulkan 语义有关,如果有任何区别的话。
假设如下:
layout(...) coherent buffer B
{
uint field;
} b;
假设该字段正在被同一着色器(或派生着色器)的其他调用通过 atomic*()
函数修改。
如果着色器调用想要从这个 field
执行原子读取(与 GLES 中的 atomicCounter()
具有相同的语义,如果这是一个 atomic_uint
),是否存在以下两者之间有什么区别(除了显然其中一个既能写又能读)?
uint read_value = b.field;
uint read_value2 = atomicAdd(b.field, 0);
直接回答问题,这两行代码生成不同的指令,具有不同的性能特征和硬件流水线使用。
uint read_value = b.field; // generates a load instruction
uint read_value2 = atomicAdd(b.field, 0); // generates an atomic instruction
- AMD disassembly can be seen in this online Shader Playground --
buffer_load_dword
对比buffer_atomic_add
- Dissecting the NVIDIA Volta GPU Architecture via Microbenchmarking --
LDG
对比ATOM
GLSL spec 部分 4.10 内存限定符指出 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内存系统中的point-of-coherence通常是last-level缓存(L2缓存),意味着所有相关访问都必须由L2缓存执行。这也意味着连贯缓冲区不能缓存在 L1 或更靠近着色器处理器的其他缓存中。现代 GPU 在二级缓存中也有专用的原子硬件;普通负载不会使用这些,但 atomicAdd(..., 0)
会通过这些。原子硬件的带宽通常低于完整的二级缓存。
SPIR-V 有一个 OpAtomicLoad
指令。据推测,无论缓冲区描述符具有什么限定符,至少有一种硬件非原子加载无法替代原子加载。
不幸的是,据我所知,没有可以转换为 OpAtomicLoad
的 Vulkan GLSL 结构。