C++ 如何在OpenCL中最有效地映射厄米(对称)矩阵的内核范围?
我正在进行一个OpenCL项目,以生成非常大的hermitian(对称)矩阵,并试图确定生成工作ID的最佳方法 厄米矩阵沿对角线对称,因此M(i,j)=M*(j,i) 以蛮力方式,for循环如下所示:C++ 如何在OpenCL中最有效地映射厄米(对称)矩阵的内核范围?,c++,matrix,opencl,C++,Matrix,Opencl,我正在进行一个OpenCL项目,以生成非常大的hermitian(对称)矩阵,并试图确定生成工作ID的最佳方法 厄米矩阵沿对角线对称,因此M(i,j)=M*(j,i) 以蛮力方式,for循环如下所示: for(int i = 0; i < N; i++) { for(int j = 0; j < N; j++) { complex<float> result = doSomeCalculation(); M(i,j) = resul
for(int i = 0; i < N; i++)
{
for(int j = 0; j < N; j++)
{
complex<float> result = doSomeCalculation();
M(i,j) = result;
}
}
for(int i=0;i
然而,利用厄米特性质,通过仅计算矩阵的上三角部分并在下三角部分复制结果,可以使循环的效率提高一倍:
for(int i = 0; i < N; i++)
{
for(int j = i; j < N; j++)
{
complex<float> result = doSomeCalculation();
M(i,j) = result;
M(j,i) = conj(result);
}
}
for(int i=0;i
在这两个循环中,doSomeCalculation()是一个昂贵的操作,矩阵中的每个条目都与其他条目完全不相关(即问题是愚蠢的并行)
我的问题是:
如何将doSomeCalculation作为OpenCL内核来实现第二个循环,以使线程ID得到最有效的利用(即,线程在不必调用doSomeCalculation()两次的情况下同时计算M(I,j)和M(j,I)?如果你的矩阵非常大,那么你可以将你的NxN矩阵切成(N/k)x(N/k)分片,每个分片的大小为kxk。只要您只需要一半的数据,就可以大致创建1DN大小范围
local\u group\u大小*(N/k)x(N/k)/2
矩阵的每个磁贴都由一个LocalGroup处理(LocalGroup的大小由您选择)。其思想是在主机端创建一个数组,其中包含矩阵中每个工作组的位置。内核存根应如下所示:
void __kernel myKernel(
__global int* coords,
....)
{
int2 WorkGroupPositionInMatrix = vload2(get_group_id(0), coords);
...
DoCalculation();
...
WriteResultTwice();
...
return;
}
你需要手工做的是处理千个工作组,这些工作组将被放置在矩阵对角线上。如果矩阵大小较大,而本地组的开销较小,则放置在对角线上的工作组可以忽略不计。一个直角三角形可以垂直切成两半,较小的部分旋转以适应较大的部分,形成一个相等的矩形a、 因此,很容易将三角形的全局工作区域变成一个适合OpenCL的矩形
请参见我的答案:您需要使用线性索引,例如,您可以通过以下方式为矩阵的每个元素编制索引:
0 1 2 ... N-1
* N-2 ... 2N-2
....
* * 2N-1 ... N(N+1)/2 -1
也就是说,指数K由下式给出:
k=iN-i*(i+1)/2+j
其中N是矩阵的大小,(i,j)分别是行和列的基于0的索引
这种关系可以颠倒;请参见问题的答案,为了完整性,我在此报告:
i = floor( ( 2*N+1 - sqrt( (2N+1)*(2N+1) - 8*k ) ) / 2 ) ;
j = k - N*i + i*(i+1)/2 ;
因此,您需要将一个1D内核与N(N+1)/2个工作项排队,您可以自己决定工作组的大小(通常每个工作组64个项目是一个不错的选择)
然后,在OpenCL代码中,您可以使用以下方法检索索引K:
int k = get_group_id(0)*64 + get_local_id(0);
然后使用需要计算的矩阵元素索引上方的两个关系
此外,请注意,您还可以通过将厄米矩阵表示为具有N(N+1)/2个元素的线性向量来节省空间。对于i==j的对角线,使用了什么公式?它是conj(result)吗?在我的例子中,对于i==j,它是标度恒等式(即标量)的特例但是,因为恒等式对角线在厄米矩阵中是纯实数,所以它是它自己的复共轭。谢谢,但是似乎有一个错误。在计算J之后,需要向它添加I以获得J的“正确”值。@stix。谢谢,修复了。