GPU的片上共享内存有限,完整的大矩阵的运算会导致内存溢出,对矩阵分块应用并行计算是必备的优化方法,那如何对矩阵分块,分块后如何并行计算?了解以上问题可以加深对涉及大量矩阵运算算法的理解。
1)如何对大矩阵进行分块,矩阵分块基于怎样的数学原理 2)分块后的矩阵如何利用多GPU核心进行并行计算,提高计算效率 3)Tile Size 分块大小如何选择,需要考虑哪些限制因素
矩阵分块核心目标是:优化内存访问效率,避免片上内存溢出。
传统的矩阵乘法直接计算完整矩阵,若矩阵规模较大,会导致片上内存(如缓存、寄存器)无法容纳所有中间数据,进而频繁访问主存,计算效率降低。
矩阵拆解的可行性基于以下两点:
矩阵乘法满足分配律与加法结合律,即对于矩阵 A,B,C 有:。扩展到分块场景,整个矩阵乘法可拆解为多个小块矩阵乘法的和。
例如,若将矩阵 A 和 B 划分为多个子矩阵(Tile),则:其中和为分块后的子矩阵,通过累加所有子矩阵的乘积,最终得到完整结果。
GPU 的 Shared Memory、CPU 缓存的片上内存容量远小于主存,分块后每次仅需在片上存储少量子矩阵,无论原矩阵规模多大,都能将内存占用控制在硬件限制内,避免溢出。
以两个 4×4 矩阵 A 和 B 相乘为例,将其划分为 2×2 的子矩阵(T=2),分块计算过程。

分块计算的执行流程(以 C11 为例)
矩阵分块代码:
def tile_matrix(matrix, tile_size):
"""将矩阵分割为多个tile"""
rows, cols = matrix.shape
tiles = []
for i inrange(0, rows, tile_size):
row_tiles = []
for j inrange(0, cols, tile_size):
# 提取一个tile
tile = matrix[i:i+tile_size, j:j+tile_size]
row_tiles.append(tile)
tiles.append(row_tiles)
return tiles分块计算代码:
def matrix_multiply_tiled(A, B, tile_size):
"""使用分块技术实现矩阵乘法"""
...
# 遍历所有tile组合进行计算
for i inrange(num_tile_rows):
for j inrange(num_tile_cols):
# 初始化当前C的tile为零矩阵
current_C_tile = np.zeros_like(C_tiles[i][j])
# 累加所有k对应的乘积
for k inrange(num_tile_k):
A_tile = A_tiles[i][k]
B_tile = B_tiles[k][j]
# 计算两个tile的乘积并累加到结果
current_C_tile += A_tile @ B_tile
# 将计算结果写回C的对应tile位置
tile_row_start = i * tile_size
tile_col_start = j * tile_size
C[tile_row_start:tile_row_start+current_C_tile.shape[0],
tile_col_start:tile_col_start+current_C_tile.shape[1]] = current_C_tile
return C通过分块,矩阵运算得以在硬件约束下高效执行,这也是现代高性能计算框架(如 CUDA、OpenBLAS)的核心优化手段之一。熟悉了矩阵分块的基本原理,再来看下不同分块是怎样并行计算,加速整个矩阵计算过程的。
在 GPU 上实现分块矩阵乘法时,主要利用 CUDA 的线程层次结构(Thread Hierarchy)将计算任务分配给大量 CUDA 核心,并通过共享内存(Shared Memory)优化数据访问效率。
CUDA 将线程组织为线程块(Thread Block)和网格(Grid):
对于分块矩阵乘法,典型的映射策略是:
以两个 4×4 矩阵相乘为例,假设使用 2×2 的分块大小(T=2),并映射到 GPU 线程。
1,矩阵分块
2,加载数据到共享内存:
3,线程并行计算:
每个线程计算 C11 中的一个元素,例如:
4,结果写回全局内存:
计算完成后,将 C11 写回全局内存。同理对于C12,C21,C22也是同样的计算过程。
整个网格包含 4 个线程块,并行计算 C 的 4 个 Tile:
这些线程块可在不同的 SM 上并行执行,大幅加速计算。
总结: GPU 上分块矩阵乘法通过以下方式实现高效并行计算:
分块大小(Tile Size)的选择,直接影响性能,太小增加内存访问次数,降低计算效率。太大可能导致共享内存溢出,或无法充分利用线程并行性。典型的 Tile Size 选择为 16×16 或 32×32,具体取决于 GPU 架构和矩阵规模。具体Tile Size 的选择考虑以下几个方面的限制
• 限制:每个 SM(Streaming Multiprocessor)的共享内存容量有限(如 NVIDIA V100 为 96KB,RTX 3080 为 64KB)
• 影响:合适的Tile Size 需保证, A 和 B 的分片后的 Tile 能同时放入共享内存
• 计算公式
每个线程块所需共享内存 = 2 × Tile Size² × sizeof(float)例如:Tile Size=32 时需 2×32×32×4B = 8KB(远小于 64KB,可行),若 Tile Size=64,则需 32KB(接近某些 GPU 限制,需谨慎)
分块技术的核心价值为:
如果使用pytorch框架进行训练或是推理,Tile size 会自动的进行调整,当手撸自定义算子时候,可能需要自己定义,对于大多数 GPU,32×32 是平衡内存和计算的良好起点。当共享内存有限或矩阵较小时,尝试 16×16 的分片。
以上主要是为大型矩阵是如何在GPU上进行分块计算,可以帮助理解模型前向和后向传播的过程,以及一些attention的优化方案。
更多精彩:
历史文章: