假设我有以下序列C:
int add(int* a, int* b, int n)
{
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
{
a[i][j]+=b[i][j];
}
}
return 0;
}
我认为解决它的最佳方法是认识到它是一个2D问题,并根据CUDA kernel - nested for loop使用2D线程块
考虑到这一点,我开始写这样的cuda kernal:
__global__ void calc(int **A, int **B, int n)
{
int i= blockIdx.x * blockDim.x + threadIdx.x;
int j= blockIdx.y * blockDim.y + threadIdx.y;
if (i>=n || j>=n)
return;
A[i][j]+=B[i][j];
}
nvcc告诉我:
./addm.cu(13): Warning: Cannot tell what pointer points to, assuming global memory space
./addm.cu(13): Warning: Cannot tell what pointer points to, assuming global memory space
./addm.cu(13): Warning: Cannot tell what pointer points to, assuming global memory space
1)我的哲学是正确的吗? 2)我认为我理解块,线程等,但我不明白是什么
int i= blockIdx.x * blockDim.x + threadIdx.x;
int j= blockIdx.y * blockDim.y + threadIdx.y;
确实
3)这是一般在2D阵列上执行操作的最有效/最快的方法吗?即不仅仅是矩阵加法,它可以是任何“逐个元素”的操作。
4)我可以从matlab调用吗?通常,当原型的形式为type** var
谢谢你们
答案 0 :(得分:5)
编译器警告你得到的结论是,在较旧的GPU上,内存结构并非“平坦”。编译器无法知道内核所使用的指针数组所拥有的地址是什么内存空间。所以它警告你它假设操作正在全局内存中进行。如果您编译Fermi卡的代码(sm_20或sm_21架构),您将看不到该警告,因为这些卡上的内存模型是“平坦的”,并且硬件在运行时正确解释了指针。编译器不需要在编译时处理它。
回答你的每个问题:
是。和不。总体思路约为90%,但有几个实施问题将从后面的答案中明显看出。
CUDA C内置了变量,允许每个线程确定其运行的执行网格中的“坐标”,以及每个块的尺寸和网格。 threadIdx.{xyz}
提供块内的线程坐标,blockIdx.{xyz}
块与网格坐标。 blockDim.{xyz}
和gridDim.{xyz}
分别提供块和网格的尺寸(注意并非所有硬件都支持3D网格)。 CUDA使用column major order对每个块中的线程进行编号,并在每个网格中进行块处理。您要查询的计算是使用线程和块坐标以及块大小计算2D网格中的等效{i,j}
坐标。在CUDA编程指南的“编程模型”一章的前几页中对此进行了详细讨论。
不,我说这有两个原因。
首先,在CUDA中使用指针数组进行内存访问并不是一个好主意。指针间接的两个级别极大地增加了获取数据的延迟惩罚。与现代CPU架构相比,典型GPU架构的主要区别在于内存系统。 GPU具有惊人的高峰值内存带宽,但访问延迟非常高,而CPU的设计延迟时间最短。因此,必须读取和间接两个指针来从内存中获取值是一个非常大的性能损失。将2D数组或矩阵存储在线性存储器中。这就是BLAS,LAPACK和Matlab所做的事情。
其次,代码中的每个线程都为每个“生产”整数运算(添加)执行四个整数算术运算的设置开销(索引计算)。有减少这种情况的策略,通常涉及让每个线程处理多个数组元素。
如果我要为该操作编写内核,我会像答案底部的代码那样做。这使用线性内存和 1D网格。适当占用GPU的线程数适当处理整个输入数组,每个线程处理许多输入。
没有。正如我在前面的回答中提到的,Matlab使用线性内存来存储矩阵,而不是指针数组。这与您的内核代码所期望的布局不匹配。
示例代码:
__global__ void calc(int *A, int *B, int N)
{
int i = blockIdx.x * blockDim.x + threadIdx.x;
int s = blockDim.x * gridDim.x;
for( ; i<N; i+=s) {
A[i] += B[i];
}
}
答案 1 :(得分:1)
我假设您正在使用n-by-n,行主要顺序数组。请尝试以下方法:
__global__ void calc(int *A, int *B, int n)
{
int i= blockIdx.x * blockDim.x + threadIdx.x;
int j= blockIdx.y * blockDim.y + threadIdx.y;
if (i<n && j<n) {
A[i*n+j] += B[i*n+j];
}
}