Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Multithreading OpenCL浮点和归约_Multithreading_Parallel Processing_Opencl_Race Condition_Reduction - Fatal编程技术网

Multithreading OpenCL浮点和归约

Multithreading OpenCL浮点和归约,multithreading,parallel-processing,opencl,race-condition,reduction,Multithreading,Parallel Processing,Opencl,Race Condition,Reduction,我想对这段内核代码(一维数据)应用reduce: 有办法吗 我对sum有一个竞争条件。要开始,您可以执行以下示例()。在这里,我们还通过使用OpenCLfloat4数据类型来利用向量处理 请记住,下面的内核返回许多部分和:每个本地工作组返回一个部分和,返回给主机。这意味着您必须通过将主机上的所有部分总和相加来执行最终总和。这是因为(至少在OpenCL1.2中)没有同步不同工作组中工作项的屏障功能 如果不希望在主机上对部分和求和,可以通过启动多个内核来解决这一问题。这会带来一些内核调用开销,但在某

我想对这段内核代码(一维数据)应用reduce:

有办法吗


我对sum有一个竞争条件。

要开始,您可以执行以下示例()。在这里,我们还通过使用OpenCLfloat4数据类型来利用向量处理

请记住,下面的内核返回许多部分和:每个本地工作组返回一个部分和,返回给主机。这意味着您必须通过将主机上的所有部分总和相加来执行最终总和。这是因为(至少在OpenCL1.2中)没有同步不同工作组中工作项的屏障功能

如果不希望在主机上对部分和求和,可以通过启动多个内核来解决这一问题。这会带来一些内核调用开销,但在某些应用程序中,额外的代价是可以接受的或无关紧要的。要在下面的示例中实现这一点,您需要修改主机代码以重复调用内核,然后在输出向量的数量低于本地大小(详细信息留给您或检查上下文)后包含停止执行内核的逻辑

编辑:为输出添加了额外的内核参数。在浮点4向量上添加点积求和

__kernel void reduction_vector(__global float4* data,__local float4* partial_sums, __global float* output) 
{
    int lid = get_local_id(0);
    int group_size = get_local_size(0);
    partial_sums[lid] = data[get_global_id(0)];
    barrier(CLK_LOCAL_MEM_FENCE);

    for(int i = group_size/2; i>0; i >>= 1) {
        if(lid < i) {
            partial_sums[lid] += partial_sums[lid + i];
        }
        barrier(CLK_LOCAL_MEM_FENCE);
    }

    if(lid == 0) {
        output[get_group_id(0)] = dot(partial_sums[0], (float4)(1.0f));
    }
}
\uuuuuu内核无效缩减向量(\uuuu全局浮点4*数据,\uuuuu局部浮点4*部分和,\uuuu全局浮点*输出)
{
int lid=get_local_id(0);
int group_size=get_local_size(0);
部分和[lid]=数据[get_global_id(0)];
屏障(CLK_本地_MEM_围栏);
对于(int i=组大小/2;i>0;i>>=1){
if(lid
减少数据的一种简单而快速的方法是重复地将数据的上半部分折叠到下半部分

例如,请使用以下极其简单的CL代码:

__kernel void foldKernel(__global float *arVal, int offset) {
    int gid = get_global_id(0);
    arVal[gid] = arVal[gid]+arVal[gid+offset];
}

使用以下java/joc主机代码(或将其端口C++等):

主机代码循环log2(n)次,因此即使使用大型阵列,也能很快完成。用“m”和“n”来处理两个数组的非幂次

  • OpenCL可以很容易地并行化任何GPU平台(即fast)
  • 内存不足,因为它可以就地工作
  • 在两种数据大小的非幂次情况下有效工作
  • 灵活,例如,您可以将内核更改为“min”而不是“+”

如果您支持OpenCL C 2.0功能,您可以在单个工作组中使用新的
工作组\u reduce\u add()
函数进行总和缩减

我知道这是一篇非常古老的帖子,但从我所做的一切来看,Bruce的答案都不起作用,由于全局内存使用和内核执行开销,来自Adam的一个是低效的

Jordan对Bruce答案的评论是正确的,即该算法在元素数量不均匀的每次迭代中都会出现故障。然而,它本质上与在多个搜索结果中可以找到的代码相同

我为此挠头了好几天,部分原因是我选择的语言不是基于C/C++的,而且在GPU上调试即使不是不可能也很棘手。但最终,我找到了一个有效的答案

这是布鲁斯的答案和亚当的答案的组合。它将源从全局内存复制到本地内存中,然后通过反复将上半部分折叠到下半部分来减少,直到没有数据留下

结果是一个缓冲区包含与所使用的工作组相同数量的项(这样可以分解非常大的缩减),必须由CPU汇总,否则从另一个内核调用,并在GPU上执行最后一步

这一部分我有点不知所措,但我相信,这段代码还通过基本上按顺序读取本地内存来避免银行切换问题。**我希望任何人都能证实这一点

注意:如果数据从偏移量0开始,则可以从源中省略全局“AOffset”参数。只需将其从内核原型和用作数组索引一部分的第四行代码中删除

__kernel void Sum(__global float * A, __global float *output, ulong AOffset, __local float * target ) {
        const size_t globalId = get_global_id(0);
        const size_t localId = get_local_id(0);
        target[localId] = A[globalId+AOffset];

        barrier(CLK_LOCAL_MEM_FENCE);
        size_t blockSize = get_local_size(0);
        size_t halfBlockSize = blockSize / 2;
        while (halfBlockSize>0) {
            if (localId<halfBlockSize) {
                target[localId] += target[localId + halfBlockSize];
                if ((halfBlockSize*2)<blockSize) { // uneven block division
                    if (localId==0) { // when localID==0
                        target[localId] += target[localId + (blockSize-1)];
                    }
                }
            }
            barrier(CLK_LOCAL_MEM_FENCE);
            blockSize = halfBlockSize;
            halfBlockSize = blockSize / 2;
        }
        if (localId==0) {
            output[get_group_id(0)] = target[0];
        }
    }
\uuuuu内核无效和(\uuuu全局浮点*A,\uuuuu全局浮点*输出,ulong AOffset,\uuuu局部浮点*目标){
const size\u t globalId=get\u global\u id(0);
const size\u t localId=get\u local\u id(0);
目标[localId]=A[globalId+AOffset];
屏障(CLK_本地_MEM_围栏);
大小\u t块大小=获取本地大小(0);
大小\u t半块大小=块大小/2;
而(半块大小>0){

如果(localIdIt称为并行缩减,请查阅。它不像您的代码片段那么简单,但也不是非常困难。只需要做更多的工作。这不是一种非常快速的方法,因为您不断地从全局内存读/写。您在本地内存中进行合并读取、执行任何操作以及然后,只有在用尽本地内存池中所有可能的计算后才回写到全局。使用本地内存可能会更快。然后,如果OpenCL平台优化对连续全局内存位置的内核访问,则可能不会。我想,没有向上投票意味着其他人不重视简单性是的!如果您感兴趣,我可以向您展示基准测试,但它要快得多;本地延迟比全局延迟低约100倍(快)。您的内核实例化也较少,这也会影响速度,而且通常值得遭受额外的银行冲突来减少它们(主要是因为全局太慢)虽然我同意你的方法很简单,但在处理OpenCL时,速度通常是优先考虑的。我很确定“if(lid__kernel void foldKernel(__global float *arVal, int offset) { int gid = get_global_id(0); arVal[gid] = arVal[gid]+arVal[gid+offset]; }
    int t = totalDataSize;
    while (t > 1) {
        int m = t / 2;
        int n = (t + 1) / 2;
        clSetKernelArg(kernelFold, 0, Sizeof.cl_mem, Pointer.to(arVal));
        clSetKernelArg(kernelFold, 1, Sizeof.cl_int, Pointer.to(new int[]{n}));
        cl_event evFold = new cl_event();
        clEnqueueNDRangeKernel(commandQueue, kernelFold, 1, null, new long[]{m}, null, 0, null, evFold);
        clWaitForEvents(1, new cl_event[]{evFold});
        t = n;
    }
__kernel void Sum(__global float * A, __global float *output, ulong AOffset, __local float * target ) {
        const size_t globalId = get_global_id(0);
        const size_t localId = get_local_id(0);
        target[localId] = A[globalId+AOffset];

        barrier(CLK_LOCAL_MEM_FENCE);
        size_t blockSize = get_local_size(0);
        size_t halfBlockSize = blockSize / 2;
        while (halfBlockSize>0) {
            if (localId<halfBlockSize) {
                target[localId] += target[localId + halfBlockSize];
                if ((halfBlockSize*2)<blockSize) { // uneven block division
                    if (localId==0) { // when localID==0
                        target[localId] += target[localId + (blockSize-1)];
                    }
                }
            }
            barrier(CLK_LOCAL_MEM_FENCE);
            blockSize = halfBlockSize;
            halfBlockSize = blockSize / 2;
        }
        if (localId==0) {
            output[get_group_id(0)] = target[0];
        }
    }