金属核功能是原子的吗?

时间:2019-04-22 22:14:17

标签: metal

可以在内核函数内部的各行之间进行上下文切换吗?

由于我在进行更改之前先设置了一些值,所以我想确保是否设置了该值,然后进行了更改。

1 个答案:

答案 0 :(得分:1)

最简单的答案是,是的,上下文切换肯定会在“两线之间”进行。这就是上下文切换的全部要点:如果着色器中的某些行(无论片段,顶点还是内核)需要一些尚不可用的资源(ALU,特殊功能单元,纹理单元,内存),GPU肯定会切换上下文。这称为 latency隐藏,它对于GPU的性能非常重要,因为如果没有它,GPU内核将大部分时间停滞在上述各种资源上。这意味着Metal内核功能绝对不是原子的。

对于您遇到的问题,如果您希望原子地进行某些操作,则有两种主要方法可以使用“金属着色”语言进行处理:

  1. 您可以使用metal_atomic标头中的原子类型和函数。这是C ++ 14 atomic标头的子集,它包含原子存储,加载,交换,比较和交换以及获取和修改功能。
  2. 您可以使用SIMD组和线程组屏障。屏障允许在允许任何线程继续之前等待组中的所有线程执行所有操作。根据传递给屏障功能的标志,屏障还可以对内存访问进行排序或将其用作执行屏障(如果您从着色器中写入一些数据,则可能不是您想要的)。

有关这些功能的更多信息,请参阅Metal Shading Language Specification(有关“线程组和SIMD-group同步功能”的信息,请参阅5.8.1;有关“原子功能”的信息,请参阅5.13)。

通常,如果您的内核函数处理了一些数据(以后需要减少这些数据),则您将执行以下操作(这是一个非常简单的示例):

kernel void
my_kernel(texture2d<half> src [[ texture(0) ]],
    texture2d<half, access::write> dst [[ texture(1) ]],
    threadgroup float *intermediate [[ threadgroup(0) ]],
    ushort2 lid [[ thread_position_in_threadgroup ]],
    ushort ti [[ thread_index_in_threadgroup ]],
    ushort2 gid [[ thread_position_in_grid ]])
{
    // Read data
    half4 clr = src.read(gid);

    // Do some work for each thread
    intermediate[ti] = intermediateResult;

    // Make sure threadhroup memory writes are visible to other threads
    threadgroup_barrier(mem_flags::mem_threadgroup);

    // One thread in the whole threadhgroup calculates some final result
    if (lid.x == 0 && lid.y == 0)
    {
        // Do some work per threadgroup
        dst.write(finalResult, gid);
    }
}

这里,线程组中的所有线程都从src纹理读取数据,执行工作,将中间结果存储在线程组内存中,然后计算最终结果并将其写出到纹理dstthreadgroup_barrier确保其他线程(包括将要计算最终结果的thread_position_in_threadgroup等于(0, 0)的线程)可以看到内存写入。