即使未在主机代码中初始化跨度,CUDA内核如何自动为矩阵设置跨度?

时间:2019-07-14 13:07:54

标签: c++ matrix cuda stride

我正在研究cuda c,我正在使用的源代码使用cuda示例程序,特别是在运行时进行矩阵乘法。 我正在逐行遵循代码并尝试预测下一步,以确保我理解代码。
在此期间,我发现具有数据成员跨度的Matrix的结构声明。
整个代码没有一行可以初始化此跨步数据成员。
我用nsight调试设备代码,并用普通vs调试器调试主机代码>>>>>有一个惊喜:
直到程序成功结束,主机代码才真正初始化该数据成员。
但是nsight甚至在第一个内核行之前就显示了跨步已初始化。
当我查看内核调用的vs调试器的自动窗口时,我注意到内核的函数名称行显示__cuda_0矩阵,其结构与程序Matrix结构相同,但具有初始化的步幅???
所以我不知道何时和谁在设备代码上初始化了这个步幅变量??? 非常感谢

这是矩阵的结构


typedef struct 
{   int width;
    int height;
    float* elements;    
    int stride;
 } Matrix;

这是不跨步初始化矩阵的主要代码

int main(int argc, char* argv[])
{
    Matrix A, B, C;
    int a1, a2, b1, b2;

    a1 = atoi(argv[1]); /* Height of A */
    a2 = atoi(argv[2]); /* Width of A */
    b1 = a2; /* Height of B */
    b2 = atoi(argv[3]); /* Width of B */

    A.height = a1;
    A.width = a2;
    A.elements = (float*)malloc(A.width * A.height * sizeof(float));

    B.height = b1;
    B.width = b2;
    B.elements = (float*)malloc(B.width * B.height * sizeof(float));

    C.height = A.height;
    C.width = B.width;
    C.elements = (float*)malloc(C.width * C.height * sizeof(float));

    for(int i = 0; i < A.height; i++)
        for(int j = 0; j < A.width; j++)
            A.elements[i*A.width + j] = (rand() % 3);//arc4random

    for(int i = 0; i < B.height; i++)
        for(int j = 0; j < B.width; j++)
            B.elements[i*B.width + j] = (rand() % 2);//arc4random

    MatMul(A, B, C);

整个代码在:CUDA C编程指南中 第3-2-3章

好吧,我到现在为止是-4,可能问题的目的不清楚:
在MatMul主机函数中,有几行用于声明和初始化所用矩阵的设备副本,并且它使用A.width来初始化d_A.stride...。

 void MatMul(const Matrix A, const Matrix B, Matrix C) 
{
 // Load A and B to device memory
 Matrix d_A;
 d_A.width = d_A.stride = A.width;
 d_A.height = A.height;
 size_t size = A.width * A.height * sizeof(float);
 cudaMalloc(&d_A.elements, size);
 cudaMemcpy(d_A.elements, A.elements, size, cudaMemcpyHostToDevice);

但是当您到达:

 // Invoke kernel
 dim3 dimBlock(BLOCK_SIZE, BLOCK_SIZE);
 dim3 dimGrid(B.width / dimBlock.x, A.height / dimBlock.y);
 MatMulKernel<<<dimGrid, dimBlock>>>(d_A, d_B, d_C);  

它调用MatMulKernel,并在此设备代码“仅取决于设备内存”中找到以下行:

// Matrix multiplication kernel called by MatMul()
__global__ void MatMulKernel(Matrix A, Matrix B, Matrix C)

{

以矩阵A作为参数...在这里,我看到了我的困惑原因!!!!
MatMulKernel使用名称A引用传递给它的d_A矩阵...
所以稍后在这些行:

    // Get sub-matrix Asub of A
    Matrix Asub = GetSubMatrix(A, blockRow, m);  

它调用另一个名为GetSubMatrix的设备函数,将A传递给它,实际上是d_A,然后在GetSubMatrix代码中使用A.stride,它实际上是d_A.stride

__device__ Matrix GetSubMatrix(Matrix A, int row, int col) 
{
    Matrix Asub;
    Asub.width    = BLOCK_SIZE;
    Asub.height   = BLOCK_SIZE;
    ***Asub.stride   = A.stride;***
    Asub.elements = &A.elements[A.stride * BLOCK_SIZE * row
                                     + BLOCK_SIZE * col];
    return Asub;
}   

因此,主机代码结构确实不会初始化A.stride
而且没有隐藏的机制可以在cuda中从类似structre的矩阵中扣除A.stride。.
但是在两种不同矩阵的主机代码和设备代码中都使用名称A引起了我的困惑。

问题解决了。

1 个答案:

答案 0 :(得分:-3)

由于名称struct Matrix的数据成员步幅未在主机代码矩阵中初始化,但是将在设备中初始化,因此使用名称A引用主机代码矩阵以及在GetSubMatrix的设备代码中引用d_A矩阵会引起混淆。复制d_A矩阵,
并且此d_A将通过已定义步幅的名为A的参数传递给GetSubMatrix。
因此,我们有2个矩阵,它们的名称为A,一个在主机未定义中,另一个在设备中定义,所以我对此有误解。

如果他们将GetSubMatrix中的参数名称从A更改为其他任何东西,就不会对跨步数据成员感到困惑。