如何在 OpenCL 中累积向量?

How to accumulate vectors in OpenCL?

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

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。

该操作称为 "reduction",似乎有一些关于它的信息 here

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

以及包含一些代码的演示文稿:link

对于 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 是实现可读性和性能的最简单方法。

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

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

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;