【问题标题】:How to accumulate vectors in OpenCL?如何在 OpenCL 中累积向量?
【发布时间】:2017-06-28 15:25:32
【问题描述】:

我有一组循环运行的操作。

for(int i = 0; i < row; i++)
{
    sum += arr1[0] - arr2[0]
    sum += arr1[0] - arr2[0]
    sum += arr1[0] - arr2[0]
    sum += arr1[0] - arr2[0]

    arr1 += offset1;
    arr2 += offset2;
}

现在我正在尝试对这样的操作进行矢量化

for(int i = 0; i < row; i++)
{
    convert_int4(vload4(0, arr1) - vload4(0, arr2));

    arr1 += offset1;
    arr2 += offset2;
}

但是如何在不使用循环的情况下将结果向量累积到标量 sum 中?

我正在使用 OpenCL 2.0。

【问题讨论】:

    标签: opencl opencl-c


    【解决方案1】:

    这个操作叫做“reduction”,上面好像有一些信息here

    在 OpenCL 中似乎实现了特殊功能,其中一个是 work_group_reduce(),它可能会对您有所帮助:link

    还有一个包含一些代码的演示文稿:link

    【讨论】:

    • 据我所知,减少是针对工作组中的工作项。然而,在我的代码中,它是一个单独的工作项。
    • “归约”是一个通用概念,其中多个变量“归约”为一个。您可以为此使用不同的操作:加法、乘法、最小、最大、异或、与、或等。链接是向您发送的一些代码,展示了如何编写高效的并行代码以实现实现。由于每种情况都不同,我不确定是否有一个简单的操作可以解决您的问题。
    【解决方案2】:

    对于 float2、float4 和类似的,最简单的版本可能是点积。 (从 int 转换为 float 可能会很昂贵)

    float4 v1=(float4 )(1,2,3,4);
    float4 v2=(float4 )(5,6,7,8);
    
    float sum=dot(v1-v2,(float4)(1,1,1,1));
    

    这等于

    (v1.x-v2.x)*1 + (v1.y-v2.y)*1+(v1.z-v2.z)*1+(v1.w-v2.w)*1 
    

    如果有任何硬件支持,让编译器摆布应该没问题。对于更大的向量,尤其是数组,J.H.Bonarius 的答案是要走的路。据我所知,只有 CPU 有这样的垂直求和运算,GPU 没有,但为了便携性,点积和 work_group_reduce 是实现可读性甚至性能的最简单方法。

    点积有额外的乘法,所以它可能并不总是很好。

    【讨论】:

    • 为什么整数不支持此操作?
    • 可能是因为软件行业。比如游戏开发者长期自己编写平方根算法,而不是向硬件厂商请教。您也可以将水平添加转换为垂直添加因此您可以像向量一样添加,但至少需要 4 5 个向量
    【解决方案3】:

    我找到了一个解决方案,这似乎是我预期的解决问题的最接近的方法。

    uint sum = 0;
    uint4 S;
    
    for(int i = 0; i < row; i++)
    {
        S += convert_uint4(vload4(0, arr1) - vload4(0, arr2));
    
        arr1 += offset1;
        arr2 += offset2;
    }
    
    S.s01 = S.s01 + S.s23;
    sum = S.s0 + S.s1;
    

    OpenCL 2.0 为向量提供了此功能,其中向量的元素可以连续替换为如上所示的加法运算。这最多可以支持大小为 16 的向量。较大的操作可以拆分为较小操作的因子。例如,要添加两个大小为 32 的向量之间差异的绝对值,我们可以执行以下操作:

    uint sum = 0;
    uint16 S0, S1;
    
    for(int i = 0; i < row; i++)
    {
        S0 += convert_uint16(abs(vload16(0, arr1) - vload16(0, arr2)));
        S1 += convert_uint16(abs(vload16(1, arr1) - vload16(1, arr2)));
    
        arr1 += offset1;
        arr2 += offset2;
    }
    
    S0 = S0 + S1;
    S0.s01234567 = S0.s01234567 + S0.s89abcdef;
    S0.s0123 = S0.s0123 + S0.s4567;
    S0.s01 = S0.s01 + S0.s23;
    sum = S0.s0 + S0.s1;
    

    【讨论】:

    • 那么 cpu 必须很快,因为没有额外的乘法,因为点积 gpu 也很快
    猜你喜欢
    • 2021-12-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-20
    • 2011-04-15
    • 2018-03-28
    相关资源
    最近更新 更多