Parallel processing 金属非原子平行还原

Parallel processing 金属非原子平行还原,parallel-processing,gpgpu,metal,Parallel Processing,Gpgpu,Metal,我刚刚进入平行缩减的世界。我正试图用金属来实现这一点。我已经能够成功地使用原子类型和原子函数来编写一个简单的版本 我现在正在尝试对非原子变量做类似的事情,一个简单的结构 定义如下: struct Point2 { int x; int y; }; 使用如下内核函数: kernel void compareX(const device Point2 *array [[ buffer(0) ]], device Point2 *result [[

我刚刚进入平行缩减的世界。我正试图用金属来实现这一点。我已经能够成功地使用原子类型和原子函数来编写一个简单的版本

我现在正在尝试对非原子变量做类似的事情,一个简单的结构

定义如下:

struct Point2
{
    int x;
    int y;
};
使用如下内核函数:

kernel void compareX(const device Point2 *array [[ buffer(0) ]],
                 device Point2 *result [[ buffer(1) ]],
                 uint id [[ thread_position_in_grid ]],
                 uint tid [[ thread_index_in_threadgroup ]],
                 uint bid [[ threadgroup_position_in_grid ]],
                 uint blockDim [[ threads_per_threadgroup ]]) {

    threadgroup Point2 shared_memory[THREADGROUP_SIZE];

    uint i = bid * blockDim + tid;
    shared_memory[tid] = array[i];

    threadgroup_barrier(mem_flags::mem_threadgroup);

    // reduction in shared memory
    for (uint s = 1; s < blockDim; s *= 2) {
        if (tid % (2 * s) == 0 && shared_memory[tid + s].x < shared_memory[tid].x) {

            shared_memory[tid] = shared_memory[tid + s];
        }
        threadgroup_barrier(mem_flags::mem_threadgroup);
    }

    if (0 == tid ) {
///THIS IS NOT CORRECT
        result[0] = shared_memory[0];
    }

}
kernel void compareX(常量设备点2*数组[[buffer(0)],
设备点2*结果[[缓冲区(1)],
uint id[[螺纹位置在网格中]],
uint tid[[线程组中的线程索引]],
uint bid[[螺纹组位置在网格中]],
uint blockDim[[每个线程组的线程数]]{
threadgroup Point2共享_内存[线程组_大小];
uint i=投标*区块DIM+tid;
共享_内存[tid]=数组[i];
threadgroup_屏障(mem_标志::mem_threadgroup);
//减少共享内存
对于(uint s=1;s
我最初认为内存复制到缓冲区或从缓冲区复制到缓冲区会出现问题,但我已经验证了从CPU/GPU到缓冲区或从CPU/GPU到结构的工作是否正常。然后我意识到这与跨线程组同步有关

CUDA的示例/文档很多,但其他方面的示例/文档很少,而且CUDA并不总能很好地转化为Metal

在没有原子类型的情况下,获得跨线程组同步的方法是什么


内核正在尝试获取输入数组中的最小值。现在,由于写入顺序的不同,执行过程中的结果会发生变化

这可能不是最正确或最好的解决方案。但这是我在挣扎了一段时间后想到的。如果其他人找到更好的解决方案,请张贴!这也可能与不同版本的金属过时

我首先尝试使用struct上的Metal语言中包含的
\u atomic
。这应该行得通。在苦苦挣扎之后,我终于检查了文档,意识到模板目前被苹果限制为bool、int和uint

然后,我尝试使用原子int来“锁定”关键比较部分,但实际上没有成功地保护关键部分。我可能对这个实现做了一些错误的事情,并且可以看到它在工作

然后我简化为返回一个索引而不是点,这允许我再次对结果使用原子int。有点作弊,还是用原子来还原但是它起作用了,所以我可以继续前进

下面是内核现在的外观:


kernel void compareX(const device Point2 *array [[ buffer(0) ]],
                     device atomic_int *result [[ buffer(1) ]],
                     uint id [[ thread_position_in_grid ]],
                     uint tid [[ thread_index_in_threadgroup ]],
                     uint bid [[ threadgroup_position_in_grid ]],
                     uint blockDim [[ threads_per_threadgroup ]]) {

    threadgroup int shared_memory[THREADGROUP_SIZE];
    uint i = bid * blockDim + tid;
    shared_memory[tid] = i;

    threadgroup_barrier(mem_flags::mem_threadgroup);

    for (uint s = 1; s < blockDim; s *= 2) {
        if (tid % (2 * s) == 0) {
            // aggregate the index to our smallest value in shared_memory
            if ( array[shared_memory[tid + s]].x < array[shared_memory[tid]].x) {
                shared_memory[tid] = shared_memory[tid + s];
            }
        }
        threadgroup_barrier(mem_flags::mem_threadgroup);
    }
    if (0 == tid ) {
        // get the current index so we can test against that
        int current = atomic_load_explicit(result, memory_order_relaxed);

        if( array[shared_memory[0]].x < array[current].x) {
            while(!atomic_compare_exchange_weak_explicit(result, &current, shared_memory[0], memory_order_relaxed, memory_order_relaxed)) {
                // another thread won. Check if we still need to set it.
                if (array[shared_memory[0]].x > array[current].x) {
                    // they won, and have a smaller value, ignore our best result
                    break;
                }
            }
        }
    }
}


内核空比较器(常量设备点2*数组[[缓冲区(0)]],
设备原子_int*结果[[缓冲区(1)],
uint id[[螺纹位置在网格中]],
uint tid[[线程组中的线程索引]],
uint bid[[螺纹组位置在网格中]],
uint blockDim[[每个线程组的线程数]]{
线程组int共享_内存[线程组_大小];
uint i=投标*区块DIM+tid;
共享_内存[tid]=i;
threadgroup_屏障(mem_标志::mem_threadgroup);
对于(uint s=1;s数组[当前].x){
//他们赢了,而且价值较小,忽略了我们的最佳结果
打破
}
}
}
}
}

这不是使用计算内核,因此它与您想要执行的操作并不完全相同,但如果您想了解一种基于片段着色器的方法,该方法在metal中实现前缀和,下面是一个链接: