2

我有一个非常简单的 cuda 内核示例,它添加了两个矩阵的相应行。我对矩阵的内存访问有疑问。我通过 mexfunction 调用内核。我们知道在 matlab 中我们有一个列优先顺序访问,而在 C/C++ 中我们有一个行优先顺序。基于 cuda 内存组织,我们在每个块和线程的网格内都有坐标 (x,y)。我试图用两种方式访问​​内核示例中的矩阵行/列主要顺序 [ 1]。在第一个内核中,如果我错了,请纠正我,有一个列主要访问,而在第二个内核中是行主要访问。两个内核都使用相同的参数、块数和帧数进行初始化。我相信使用矩阵的行主顺序访问的第二个内核将是访问矩阵的正确方法,就像我们在 c++ 中一样。不幸的是,具有列优先顺序的内核根据算法返回正确的结果。有人有很好的解释吗?这个观察结果是否与我们通过 mexfunction 调用内核的事实有关,这意味着 matlab,因此是列主要顺序访问?

两个内核都称为:

int numElements =  rows * cols; // rows and cols of d_A or d_B
int threadsPerBlock = 16;
int blocksPerGrid = ceil( (double) (numElements) / threadsPerBlock);
dim3 dimBlock( threadsPerBlock,threadsPerBlock ); 
dim3 dimGrid( blocksPerGrid, blocksPerGrid ); 
cudaEuclid<<<dimGrid, dimBlock>>>( d_A, d_B, d_C, rows, cols );

内核 1:(工作但不是行主要的 C++ 风格)

 __global__ void cudaEuclid( float* A, float* B, float* C, int rows, int cols )
{
        int i, squareeucldist = 0;
        int r = blockDim.x * blockIdx.x + threadIdx.x; // rows
        int c = blockDim.y * blockIdx.y + threadIdx.y; // cols 


        if( r < rows  ){
            for ( i = 0; i < cols; i++ )
                            //column-major order
                squareeucldist  += ( A[r + rows*i] - B[r + rows*i] ) * ( A[r + rows*i] - B[r + rows*i] );
            C[r] = squareeucldist;
            squareeucldist = 0;
        }
}   

内核 2:(行主要顺序,c++ 风格)

__global__ void cudaEuclid( float* A, float* B, float* C, int rows, int cols )
    {
        int i, squareeucldist = 0;
        int c = blockDim.x * blockIdx.x + threadIdx.x; // cols
        int r = blockDim.y * blockIdx.y + threadIdx.y; // rows


        if( r < rows  ){
            for ( i = 0; i < cols; i++ )
                            //row-major order
                squareeucldist  += ( A[i + cols*r] - B[i + cols*r] ) * ( A[i + cols*r] - B[i + cols*r] );
            C[r] = squareeucldist;
            squareeucldist = 0;
    }
4

2 回答 2

1

这个观察结果是否与我们通过 mexfunction 调用内核的事实有关,这意味着 matlab,因此是列主要顺序访问?

是的。

对此进行扩展,我的意思是说您已经证明,没有理由不能在具有简单一维缓冲区的 C/C++ 中使用列主要约定(CUDA 的使用与您的情况无关)。

将 MATLAB 数组视为一个特殊的类,它恰好将数据缓冲区保持在列优先顺序。它实际上是mxArray在后台调用的,您可以通过 MATLAB 使用format debug.

>> format debug
>> x = [1 2 3; 4 5 6]
x =

Structure address = a91d8a0 
m = 2
n = 3
pr = 7406f620 
pi = 0
     1     2     3
     4     5     6

在地址处有一个缓冲区,pr并且mxArray知道它有m=2行和n=3列。因为它是 MATLAB,x(2)4不是2C 中的第二个值的约定。在 C 中,如果您将此二维数组定义为int A[2][3] = { {1, 2, 3}, {4, 5, 6} };则值将布置为1 2 3 4 5 6

但是,如果您有一个简单的一维缓冲区,您可以通过计算行和列的线性索引来访问它,那么没有什么可以阻止您更改约定。在您的 C 示例中,您只是在使用缓冲区(例如float* A),因此如何索引它取决于您(A[r + rows*c]vs. A[c + cols*r])。

长话短说,要么在 MATLAB 中转置并使用行优先内核,要么不理会 MATLAB 输入并使用列优先内核。

于 2013-10-09T22:03:40.130 回答
1

正如您所提到的,Matlab 使用列优先排序,因此某个矩阵(例如A)将相应地存储在 CPU 内存中。

在程序中的某个时刻,您需要A从主机内存移动到设备内存cudaMemcpy。因此,A将以列优先顺序存储在设备内存中,并且必须考虑到这一点来读取。

显然,您可以通过转置矩阵虚构地在 Matlab 中实现行优先排序存储。这对于实现合并的内存访问可能具有一些优势。

于 2013-10-10T09:24:59.490 回答