opencl内核实现了一个简单的数学公式 当实现一个定义为的错误函数时,要考虑的最佳实践是什么?
使用OpenCL内核 A、 B和C是3D浮点数组,\delta是Kronecker delta (N,M)=(2,7)或(N,M)=(3,23)的典型值 朴素的实现(如下所示)比CPU版本慢几个数量级 谢谢 Topencl内核实现了一个简单的数学公式 当实现一个定义为的错误函数时,要考虑的最佳实践是什么?,opencl,Opencl,使用OpenCL内核 A、 B和C是3D浮点数组,\delta是Kronecker delta (N,M)=(2,7)或(N,M)=(3,23)的典型值 朴素的实现(如下所示)比CPU版本慢几个数量级 谢谢 T 有什么建议吗?通常情况下,您会希望打破一些循环。。。很多 -外部循环被分成多个工作组s,这些工作组在各自的计算单元上运行(每个GPU大约有16个计算单元,不多) -接下来的几个循环将在每个工作组中的不同线程上进行拆分 如果您试图同时运行所有计算,它们将同时尝试将数据加载到内存中,这将造成
有什么建议吗?通常情况下,您会希望打破一些循环。。。很多 -外部循环被分成多个
工作组
s,这些工作组在各自的计算单元上运行(每个GPU大约有16个计算单元,不多)
-接下来的几个循环将在每个工作组中的不同线程上进行拆分
如果您试图同时运行所有计算,它们将同时尝试将数据加载到内存中,这将造成可怕的冲击。GPU的内存非常有限。当然,全局内存听起来足够大,几GB,但是全局GPU内存很慢。您希望将数据放入本地内存,每个计算单元的内存大小为32-64KB,仅此而已
您通常希望以某种方式将任务划分为非常小的任务,并为每个工作组执行以下操作:
- 将一块内存从全局内存加载到本地内存
- 整个工作组线程扭曲都可以使用coallesced访问参与复制
- 做一些记忆工作,比如做一些算术,等等
- 将结果写回全局内存
- 然后,可以迭代一点,或者干脆退出,让其他工作组处理其他工作
在CPU上,数学运算往往是一个主要的瓶颈,但在GPU上,通常内核都在无用地旋转,同时等待数据从全局内存逐渐到达。无论您如何优化这个过程,防止需求冲突,等等,都会使内核大大加快。设备的体系结构是什么?如果是带有向量寄存器的英特尔,您可以使用SIMD寄存器获得更快的速度,速度高达5倍“naive”版本作为单个工作项运行?如果是这样的话,您将浪费GPU总算术峰值容量的99.8%,因此它的速度很慢也就不足为奇了。…@talonmies:不,每个工作项都处理一个单独的问题实例。@user92382有多少个工作项?(即,索引的范围是什么?)如果不是几百或数千,那么考虑将一些循环,特别是短矩阵步幅,跨越2D或3D工作项。@ PopJorda:索引在20000到500000范围内。
__kernel void cl_bilinear_alg(
__global float * A,
__global float * B,
__global float * C,
__global const int M,
__global const int N,
__global float * R)
{
int index = get_global_id(0);
int N2 = N * N;
int mat_offset = index * N2 * M;
float s1, s2, err = 0.0f;
for (int i = 0; i < N; ++i)
{
for (int j = 0; j < N; ++j)
{
for (int k = 0; k < N; ++k)
{
for (int l = 0; l < N; ++l)
{
for (int m = 0; m < N; ++m)
{
for (int n = 0; n < N; ++n)
{
s1 = (n == i) * (j == k) * (l == m);
s2 = 0;
for (int r = 0; r < M; ++r)
{
s2 += A[mat_offset + r * N2 + i * N + j] *
B[mat_offset + r * N2 + k * N + l] *
C[mat_offset + r * N2 + m * N + n];
}
err += (s2 - s1) * (s2 - s1);
}
}
}
}
}
}
R[index] = err;
}
__kernel void cl_bilinear_alg(__global const float * A,
__global const float * B,
__global const float * C,
__global const int N,
__global const int M,
__global const float * kron,
__global float * R)
{
__private int index = get_global_id(0);
__private int cM = ceil(M / 4.0f);
__private int N2 = N*N;
__private int N4 = N2*N2;
__private int mat_offset = index * N2 * M;
__private float s1, s2, err = 0;
__private float4 vzero = (float4) (0.0f, 0.0f, 0.0f, 0.0f);
__local float4 va[54], vb[54], vc[54];
for (int ij = 0, k = 0; ij < N2; ++ij)
{
int r = 0;
for (; r < M / 4; r += 4, ++k)
{
int idx0 = mat_offset + N2 * r + ij;
int idx1 = mat_offset + N2 * (r + 1) + ij;
int idx2 = mat_offset + N2 * (r + 2) + ij;
int idx3 = mat_offset + N2 * (r + 3) + ij;
va[k] = (float4) (A[idx0], A[idx1], A[idx2], A[idx3]);
vb[k] = (float4) (B[idx0], B[idx1], B[idx2], B[idx3]);
vc[k] = (float4) (C[idx0], C[idx1], C[idx2], C[idx3]);
}
if (M % 4)
{
float buffa[4] = {0}, buffb[4] = {0}, buffc[4] = {0};
for (; r < M; ++r)
{
int idx = mat_offset + N2 * r + ij;
buffa[r % 4] = A[idx];
buffb[r % 4] = B[idx];
buffc[r % 4] = C[idx];
}
va[k] = vload4(0, buffa);
vb[k] = vload4(0, buffb);
vc[k++] = vload4(0, buffc);
}
}
for (int ij = 0; ij < N2; ++ij)
{
for (int kl = 0; kl < N2; ++kl)
{
for (int mn = 0; mn < N2; ++mn)
{
s1 = kron[ij * N4 + kl * N2 + mn];
s2 = 0;
for (int r = 0; r < cM; ++r)
s2 += dot(va[cM * ij + r], mad(vb[cM * kl + r], vc[cM * mn + r], vzero));
//the most expensive line
err += (s2 - s1) * (s2 - s1);
}
}
}
R[index] = err;
}
err += (s2 - s1) * (s2 - s1);