CUDA grid/block 到矩阵映射示例(矩阵加法)

CUDA grid/block 到矩阵映射示例(矩阵加法)

给一个例子 grid block 到矩阵的映射 ,以矩阵加法来表示

我们用一个非常具体、可以直接运行脑补的例子,把 CUDA 里的 grid / block → 矩阵索引彻底讲清楚,并用矩阵加法来表达。

一、问题设定(最标准的场景)

我们做一个矩阵加法:

𝐶 = 𝐴 + 𝐵

假设矩阵大小是:

𝐴, 𝐵, 𝐶 ∈ 𝑅⁸ˣ⁸

二、CUDA 配置

我们这样划分线程:

c 复制代码
dim3 block(4, 4);   // 每个 block 4x4 线程
dim3 grid(2, 2);    // 2x2 个 block

👉 总线程数:

x方向:2 × 4 = 8

y方向:2 × 4 = 8

刚好覆盖整个矩阵。

三、核心映射公式(最重要)

线程 → 矩阵坐标:

c 复制代码
int col = blockIdx.x * blockDim.x + threadIdx.x;
int row = blockIdx.y * blockDim.y + threadIdx.y;

👉 对应关系:

col = 𝐵ₓ ⋅ 𝑏ₓ + 𝑡ₓ

row = 𝐵ᵧ ⋅ 𝑏ᵧ + 𝑡ᵧ

四、直观展开(关键理解)

我们把整个 8×8 矩阵分成 4 个 block 区域

Block 分布

五、一个 block 内的映射

举例:block(1,0)

blockIdx.x = 1

blockIdx.y = 0

线程范围:

threadIdx.x ∈ [0,3]

threadIdx.y ∈ [0,3]

映射到矩阵:

col = 1*4 + tx → [4,7]

row = 0*4 + ty → [0,3]

👉 覆盖矩阵区域:

行: 0~3

列: 4~7

六、完整矩阵映射图(核心)

矩阵 C (8x8)

复制代码
矩阵 C (8x8)

      col →
      0 1 2 3 | 4 5 6 7
row ---------------------
0     A A A A | B B B B
1     A A A A | B B B B
2     A A A A | B B B B
3     A A A A | B B B B
      -----------------
4     C C C C | D D D D
5     C C C C | D D D D
6     C C C C | D D D D
7     C C C C | D D D D

对应 block:

区域 blockIdx
A (0,0)
B (1,0)
C (0,1)
D (1,1)

七、矩阵加法的 CUDA 表达

每个线程做一件事:

c 复制代码
C[row][col] = A[row][col] + B[row][col];

👉 数学表达:

𝐶ᵢⱼ = 𝐴ᵢⱼ + 𝐵ᵢⱼ

👉 CUDA 展开后本质:

𝐶(𝐵ᵧ𝑏ᵧ+𝑡ᵧ, 𝐵ₓ𝑏ₓ+𝑡ₓ) = 𝐴... + 𝐵...

八、完整 CUDA Kernel 示例

c 复制代码
__global__ void matAdd(float* A, float* B, float* C, int N) {
    int col = blockIdx.x * blockDim.x + threadIdx.x;
    int row = blockIdx.y * blockDim.y + threadIdx.y;

    if (row < N && col < N) {
        int idx = row * N + col;
        C[idx] = A[idx] + B[idx];
    }
}

九、本质总结(你要抓住这个)

你可以把整个 CUDA 映射理解成:

grid = 把大矩阵切块

block = 每个块内部再细分

thread = 每个元素一个计算单元

数学上就是:

(𝑖,𝑗)=(blockIdx.y,blockIdx.x)⊗(blockDim.y,blockDim.x)+(threadIdx.y,threadIdx.x)

十、再往深一层(工程本质)

你其实已经触碰到一个更本质的东西:

👉 CUDA 就是在做:

离散空间 → 并行计算单元 的映射函数

这个映射决定:

memory coalescing(是否连续访问)

warp 效率

cache 命中

bank conflict

如果你愿意,我可以帮你再往下一层拆:

👉 从这个例子直接推到:

warp 是怎么切的(32线程怎么落在矩阵上)

为什么 (x方向连续) 才 coalesced

为什么很多 kernel 要 transpose

这个地方一旦打通,你对 GPU 的理解会从"会用"变成"能设计"。

给出完整可执行代码

已思考若干秒

下面给你一份完整可执行的 CUDA 矩阵加法示例,重点展示:

grid 和 block 如何映射到矩阵

每个线程如何对应一个矩阵元素

最后把结果打印出来验证

这份代码可以直接保存为 mat_add.cu 编译运行。

完整代码

c 复制代码
#include <iostream>
#include <vector>
#include <cuda_runtime.h>

#define CHECK_CUDA(call)                                                    \
do {                                                                        \
    cudaError_t err = call;                                                 \
    if (err != cudaSuccess) {                                               \
        std::cerr << "CUDA error at " << __FILE__ << ":" << __LINE__        \
                  << " code=" << err                                         \
                  << " \"" << cudaGetErrorString(err) << "\"" << std::endl; \
        exit(EXIT_FAILURE);                                                 \
    }                                                                       \
} while (0)

// 打印矩阵
void printMatrix(const std::vector<float>& mat, int rows, int cols, const char* name) {
    std::cout << name << " =" << std::endl;
    for (int r = 0; r < rows; r++) {
        for (int c = 0; c < cols; c++) {
            std::cout << mat[r * cols + c] << "\t";
        }
        std::cout << std::endl;
    }
    std::cout << std::endl;
}

// CUDA Kernel: 矩阵加法
__global__ void matAddKernel(const float* A, const float* B, float* C, int rows, int cols) {
    // block 内线程坐标 + block 坐标 -> 全局矩阵坐标
    int col = blockIdx.x * blockDim.x + threadIdx.x;  // 列
    int row = blockIdx.y * blockDim.y + threadIdx.y;  // 行

    if (row < rows && col < cols) {
        int idx = row * cols + col;
        C[idx] = A[idx] + B[idx];

        // 打印映射关系
        printf("block=(%d,%d), thread=(%d,%d) -> matrix(%d,%d), A=%.1f, B=%.1f, C=%.1f\n",
               blockIdx.x, blockIdx.y,
               threadIdx.x, threadIdx.y,
               row, col,
               A[idx], B[idx], C[idx]);
    }
}

int main() {
    // 矩阵大小
    const int rows = 8;
    const int cols = 8;
    const int size = rows * cols;
    const size_t bytes = size * sizeof(float);

    // Host 端矩阵
    std::vector<float> h_A(size);
    std::vector<float> h_B(size);
    std::vector<float> h_C(size, 0.0f);

    // 初始化矩阵
    // A: 0,1,2,3...
    // B: 全部 100
    for (int r = 0; r < rows; r++) {
        for (int c = 0; c < cols; c++) {
            int idx = r * cols + c;
            h_A[idx] = static_cast<float>(idx);
            h_B[idx] = 100.0f;
        }
    }

    printMatrix(h_A, rows, cols, "A");
    printMatrix(h_B, rows, cols, "B");

    // Device 指针
    float *d_A = nullptr, *d_B = nullptr, *d_C = nullptr;
    CHECK_CUDA(cudaMalloc(&d_A, bytes));
    CHECK_CUDA(cudaMalloc(&d_B, bytes));
    CHECK_CUDA(cudaMalloc(&d_C, bytes));

    // 拷贝到 GPU
    CHECK_CUDA(cudaMemcpy(d_A, h_A.data(), bytes, cudaMemcpyHostToDevice));
    CHECK_CUDA(cudaMemcpy(d_B, h_B.data(), bytes, cudaMemcpyHostToDevice));

    // 配置 block 和 grid
    dim3 block(4, 4);  // 每个 block 4x4 个线程
    dim3 grid((cols + block.x - 1) / block.x,
              (rows + block.y - 1) / block.y);

    std::cout << "Launch config:" << std::endl;
    std::cout << "block = (" << block.x << ", " << block.y << ", " << block.z << ")" << std::endl;
    std::cout << "grid  = (" << grid.x  << ", " << grid.y  << ", " << grid.z  << ")" << std::endl;
    std::cout << std::endl;

    // 启动 Kernel
    matAddKernel<<<grid, block>>>(d_A, d_B, d_C, rows, cols);
    CHECK_CUDA(cudaGetLastError());
    CHECK_CUDA(cudaDeviceSynchronize());

    // 拷回 Host
    CHECK_CUDA(cudaMemcpy(h_C.data(), d_C, bytes, cudaMemcpyDeviceToHost));

    printMatrix(h_C, rows, cols, "C = A + B");

    // 验证结果
    bool ok = true;
    for (int i = 0; i < size; i++) {
        float expected = h_A[i] + h_B[i];
        if (h_C[i] != expected) {
            std::cerr << "Mismatch at index " << i
                      << ", got " << h_C[i]
                      << ", expected " << expected << std::endl;
            ok = false;
            break;
        }
    }

    if (ok) {
        std::cout << "Result check passed." << std::endl;
    } else {
        std::cout << "Result check failed." << std::endl;
    }

    // 释放显存
    CHECK_CUDA(cudaFree(d_A));
    CHECK_CUDA(cudaFree(d_B));
    CHECK_CUDA(cudaFree(d_C));

    return 0;
}

编译方式

如果你本机有 CUDA 编译器 nvcc,直接编译:

bash 复制代码
nvcc mat_add.cu -o mat_add

运行:

bash 复制代码
./mat_add

你会看到什么

这个程序会先打印矩阵 A 和 B,然后 GPU 里每个线程都会打印类似:

plain 复制代码
block=(1,0), thread=(2,3) -> matrix(3,6), A=30.0, B=100.0, C=130.0

这句话的意思就是:

当前线程属于 blockIdx=(1,0)

它在线程块内的位置是 threadIdx=(2,3)

它最终映射到矩阵元素 (row=3, col=6)

执行了 C[3][6] = A[3][6] + B[3][6]

这里最核心的两行

c 复制代码
int col = blockIdx.x * blockDim.x + threadIdx.x;
int row = blockIdx.y * blockDim.y + threadIdx.y;

这就是 grid/block 到矩阵坐标的映射公式。

也就是:

(𝑟𝑜𝑤,𝑐𝑜𝑙)=(blockIdx.y⋅blockDim.y+threadIdx.y, blockIdx.x⋅blockDim.x+threadIdx.x)

这份例子里的映射关系

这里设置的是:

c 复制代码
dim3 block(4, 4);
dim3 grid(2, 2);

所以整个 8×8 矩阵会被分成 4 个 block:

block(0,0) 处理左上角 4×4

block(1,0) 处理右上角 4×4

block(0,1) 处理左下角 4×4

block(1,1) 处理右下角 4×4

也就是:

如果你只是想看最精简版本

把 kernel 里的 printf 去掉就行:

c 复制代码
__global__ void matAddKernel(const float* A, const float* B, float* C, int rows, int cols) {
    int col = blockIdx.x * blockDim.x + threadIdx.x;
    int row = blockIdx.y * blockDim.y + threadIdx.y;

    if (row < rows && col < cols) {
        int idx = row * cols + col;
        C[idx] = A[idx] + B[idx];
    }
}

这样更接近实际工程代码。

补一句工程上常见的理解

二维矩阵在内存里其实还是一维存放,所以要写:

c 复制代码
int idx = row * cols + col;

这就是把二维坐标 (row, col) 映射成线性地址。

下一步如果你愿意,我可以直接给你一份:

"把每个 block 画成矩阵分块图,并逐个列出 thread 到元素映射表"的版本,那个会更直观。把这个改成一个 markdown 文件下载

(注:文档部分内容可能由 AI 生成)

相关推荐
智慧景区与市集主理人2 小时前
巨有科技:文旅二消的增收密码,数智化让“一次游览”变“多次消费”
大数据·人工智能·科技
星马梦缘2 小时前
强化学习实战8.1——用PPO打赢星际争霸【环境配置与下位机代码】
人工智能·python·jupyter·强化学习·星际争霸·stablebaseline3·starcraft2
电子科技圈2 小时前
芯科科技2026 Tech Talks技术讲座启航聚焦无线与边缘 AI,共绘智能物联新蓝图
人工智能·嵌入式硬件·mcu·物联网·智能家居·智能硬件·iot
程序员cxuan2 小时前
为什么 Claude 要求实名认证?
人工智能·后端·程序员
YuanDaima20482 小时前
Python 数据结构与语法速查笔记
开发语言·数据结构·人工智能·python·算法
key_3_feng2 小时前
基于AI智能体的防火墙策略智能管理方案
人工智能·ai智能体
神州数码云基地2 小时前
AI助手语音交互:从技术到体验
人工智能·ai·语音识别·前端开发·tts·ai语音
EasyGBS2 小时前
国标GB28181视频平台EasyGBS新增HLS按需切片:告别资源空转,让安防视频效率拉满!
网络·人工智能·音视频
keira6742 小时前
你认为什么是好的提示词?可以优化AI输出的信息
人工智能