即使未在主机代码中初始化步幅,cuda 内核也会自动为矩阵设置步幅吗?

do cuda kernel sets stride for matrix automatically even if stride not initialized in host code?

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

这是矩阵的结构


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 并在此设备代码中 "which depends only on device memory" 您会发现这些行:

// 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 中的结构一样从矩阵中扣除 A.stride ..
但是在 2 个不同矩阵的主机代码和设备代码中使用名称 A 导致我感到困惑。

问题已解决。

在 GetSubMatrix 的设备代码中使用名称 A 来引用主机代码矩阵并在设备代码中引用 d_A 矩阵导致混淆,因为 struct Matrix 的数据成员步长未在主机代码矩阵中初始化,但它将在设备副本d_A矩阵中初始化,
并且此 d_A 将通过名为 A 的参数传递给 GetSubMatrix,该参数定义了步幅。
所以我们有 2 个矩阵,名称为 A,一个在主机中未定义,另一个在设备中已定义,所以我有这种误解。

如果他们将 GetSubMatrix 中的参数名称从 A 更改为任何其他名称,则不会对 stride 数据成员造成混淆。