【问题标题】:matrix row/column-major access in kernel called by mexfunctionmexfunction 调用的内核中的矩阵行/列主要访问
【发布时间】:2013-10-09 21:25:34
【问题描述】:

我有一个非常简单的 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;
    }

【问题讨论】:

    标签: cuda parallel-processing gpu mex


    【解决方案1】:

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

    是的。

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

    将 MATLAB 数组视为一个特殊的类,它恰好将数据缓冲区保持在列优先顺序。它实际上被称为 mxArray 在引擎盖下,您可以使用 format debug 仅通过 MATLAB 获得详细信息。

    >> 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 而不是 2 在 C 中对于第二个值的约定。在 C 中,如果您将此二维数组定义为 int A[2][3] = { {1, 2, 3}, {4, 5, 6} };, the values will be laid out as 1 2 3 4 5 6

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

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

    【讨论】:

    • 你的意思是,如果我们通过 c++ main 函数而不是 mexfunction 调用内核,那么内核 2 应该返回预期的结果?
    • 这取决于您决定如何在 C++ 函数中组织数据。没有什么能阻止您在 C++ 中采用以列为主的布局——这不是存储矩阵的传统方式。
    【解决方案2】:

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

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-04-17
      • 2020-05-18
      • 2012-12-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多