如何在 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;
我有一组循环中的操作运行。
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;