想象你是一支施工队的队长,任务是盖一面巨大的“矩阵乘法之墙”(GEMM)。传统工地一块砖一块砖地砌,慢得让人心焦。突然,旁边开了一家叫 cuTLASS 的预制构件厂:它不直接派工人帮你砌墙,而是把“梁、板、柱”按标准规格生产好,任你挑选。最妙的是其中有一种“一体化钢印机”构件——Tensor Core——只要按一下,就把一小块矩阵的乘法和加法同时完成。你只需填写一张规格单(模板参数),厂房就在编译期把最合适的板材浇铸出来。于是你的工作从“搬砖”升级为“选板、拼装、安排流水线”,这就是 cuTLASS 的哲学:把控制权交到你手上,把性能压榨到极致。本文就跟着这支施工队,看看 cuTLASS 如何用模板仓库驾驭 GEMM,让 GPU 的每个计算单元都满负荷运转。

岗位关联

对 AI 从业者而言,掌握 cuTLASS 等于拥有为模型定制极致矩阵运算的能力,能让推理和训练突破通用库的性能天花板。

目录
  1. 可视化演示
  2. 初识 cuTLASS:一间预制构件仓库
  3. 模板化设计:一张规格单的魔法
  4. Tensor Core 原理:一体化钢印机
  5. GEMM 执行流程:运砖、砌墙、抹灰
  6. 分块与流水线:让工位转起来
  7. Kernel 选择策略:看菜下饭
  8. 性能调优:拧螺丝的艺术
  9. AI 应用场景
  10. Python 演示
  11. 常见误区
  12. 面试题

可视化演示

展示系统处理流水线、实时性能指标曲线及多方案对比分析,包含数据流转、吞吐延迟监控与方案评估。

本节配有交互动效(进入视口后自动加载,约 3KB)。

系统可视化 处理流程:输入→处理→模型→输出 性能监控:实时指标 P=L×T

初识 cuTLASS:一间预制构件仓库

施工队要盖墙,第一件事是逛建材市场。cuBLAS 就像“成品墙专卖店”:你报上尺寸,它直接给你一堵封好的墙,性能不错但完全不能拆开看,更别说改内部结构。而 cuTLASS 是隔壁新开的“预制构件仓库”,货架上整整齐齐摆着梁、板、柱,每种构件都标明了规格、承重和适用场景,你可以自由搭配,甚至按自己的图纸定制新构件。

在 GPU 的世界里,cuBLAS 是 NVIDIA 官方的高性能线性代数库,闭源、即调即用,像成品墙一样省心;cuTLASS(CUDA Templates for Linear Algebra)则是同一家公司的开源模板库,专注 GEMM(通用矩阵乘法)这条“盖墙主线”,把数据搬移、分块、计算、写回每个环节都做成可替换的积木。

为什么 GEMM 如此重要?因为全连接层、卷积、注意力机制,最终都要落到矩阵乘加上。cuTLASS 的目标是让开发者像搭积木一样,为特定形状的矩阵“定做”最优计算方案。它的核心公式就是下面这个 GEMM 定义——它既定义了任务,也是所有优化围绕的中心。

所以 cuTLASS 不是又一家黑盒库,而是“看得见、改得动”的构件仓库。了解它与 cuBLAS 的分工之后,下一个关键问题是:这些构件为什么能做到又快又灵活?答案藏在“规格单”——模板化设计里。

C=α×A×B+β×CC = \alpha \times A \times B + \beta \times C
这就是 GEMM 的完整定义:C 矩阵等于 A 乘 B 再缩放,并叠加在原有 C 上,而不是简单覆盖。
  1. 输入矩阵 A 与 B这是参与乘法的两个输入矩阵,A 的尺寸是 M 行 K 列,B 的尺寸是 K 行 N 列,K 是内维。
    A, BA,\ B
  2. 矩阵乘法 A×BA 的每一行与 B 的每一列做点积,得到 M 行 N 列的中间结果,这是整个计算的主体。
    A×BA \times B
  3. 缩放系数 α给乘积结果乘以一个标量 α,相当于施工图纸上的整体缩放比例,用于归一化或加权。
    α×(A×B)\alpha \times (A \times B)
  4. 累加项 β×Cβ 控制原有 C 保留多少,最后叠加到新结果上,这就是 GEMM 中“加”的来历,也是累加缓冲存在的意义。
    +β×C+ \beta \times C

模板化设计:一张规格单的魔法

在预制构件厂,最神奇的是“按单生产”。你递上一张规格单:板材尺寸、承重等级、要不要防火涂层,车间就照着铸造。施工队不用在工地上临时切割,到了现场直接拼装,一句多余的裁切都不发生。

cuTLASS 把这张规格单做成了 C++ 模板参数。分块大小 TILE_M、TILE_N、TILE_K,数据类型(FP16/BF16/FP32),流水线深度,线程数——全部写在模板尖括号里,编译期就被展开成具体的机器码。这就是“编译期配置”:程序还没跑,方案已经定死,运行时不剩任何判断分支。

模板化的好处是两头都占:既要灵活性(每种场景都能定制),又要性能(定制结果零运行时开销)。编译器看到常量分块尺寸后,会做循环展开、寄存器重排等优化。代价是编译时间变长、生成的二进制变大——就像工厂每种规格都要单独开一套模具。

规格单上的分块数量,告诉施工队整个工地要分成多少块砖。模板参数选得对不对,直接决定这面墙是“快工出细活”还是“返工重来”。下一节看最核心的构件——Tensor Core 钢印机——如何在单次按压中完成一整套乘加。

tiles=MTm×NTn×KTk\text{tiles} = \frac{M}{T_m} \times \frac{N}{T_n} \times \frac{K}{T_k}
一张规格单决定整面墙要切出多少块小砖:三个方向的分块数相乘,就是流水线要走的全部轮数。
  1. 行方向分块数M 除以行方向分块尺寸 Tm,得到纵向要切几块,对应 C 矩阵的行分块。
    MTm\frac{M}{T_m}
  2. 列方向分块数N 除以列方向分块尺寸 Tn,得到横向要切几块,对应 C 矩阵的列分块。
    NTn\frac{N}{T_n}
  3. 深度方向分块数K 除以深度方向分块尺寸 Tk,得到 K 维循环要迭代多少轮,每轮喂给 Tensor Core 一小片数据。
    KTk\frac{K}{T_k}
  4. 总块数相乘三个方向的块数相乘,就是整个 GEMM 要处理的小砖块总数,也是调度器分配任务的依据。
    MTm×NTn×KTk\frac{M}{T_m} \times \frac{N}{T_n} \times \frac{K}{T_k}

Tensor Core 原理:一体化钢印机

传统砌墙,工人要“先抹灰、再放砖、再压实”三步走。老式 GPU 算矩阵乘也一样:一个时钟周期只能做一次标量乘加。cuTLASS 仓库里最抢手的构件是 Tensor Core——它像一台一体化钢印机,一次按压就把一小块矩阵的乘法和加法全部完成,不需要中间步骤。

Tensor Core 执行的是 MMA(矩阵乘累加)指令,典型形状如 m16n8k16:A 取 16×16,B 取 16×8,一次算出 16×8 的 D,同时把旧的 C 加进去,即 D = A×B + C。对 FP16 输入用 FP32 累加,这就是深度学习混合精度训练的底气——输入紧凑、累加精准,速度和精度可以兼得。

与普通 CUDA 核心对比:一个 SM(流式多处理器)里的 Tensor Core 吞吐是普通核心的十几倍。cuTLASS 通过 wmma 等 API 把这些指令封装成模板组件,开发者不需要手写底层汇编,只要指定形状和精度,编译器就生成对应的 MMA 代码。

钢印机的力量再大,也得有砖料源源不断地送来。下一节看看 cuTLASS 的施工流程——怎么把大矩阵拆成钢印机能吃下的小块,再一块块喂进去。

D=A×B+CD = A \times B + C
钢印机的一次按压:两个小矩阵相乘,再与累加缓冲相加,结果一步到位写回 D。
  1. 输入小矩阵 A 和 B来自大矩阵的两个分块,形状必须符合 MMA 规格(如 16×16 与 16×8),就像钢印机的模具大小固定。
    A, BA,\ B
  2. 矩阵乘法两个分块相乘得到临时结果,一次指令完成全部乘加,而非逐元素循环。
    A×BA \times B
  3. 加上累加块 C把旧累加块 C 一起加上,实现多次 K 分块的连续累积,保证最终结果正确。
    +C+ C
  4. 输出 D结果写回 D,在下一轮 K 迭代中 D 又充当 C 的角色,形成“接力累加”。
    D=A×B+CD = A \times B + C

GEMM 执行流程:运砖、砌墙、抹灰

盖一面大墙,施工队不会把整面墙一次浇筑,而是分成小段:运砖的运砖,砌墙的砌墙,抹灰的抹灰。cuTLASS 执行 GEMM 也是这套流程:先把矩阵切成瓷砖大小的分块,搬上“桌面”(共享内存),再让 Tensor Core 一块块乘加,最后把结果写回显存。

具体来说,每个线程块(thread block)负责 C 矩阵中的一个分块。它会沿 K 维循环:每一轮从全局显存加载 A 的一个条带和 B 的一个条带到共享内存,调用 MMA 做乘累加,结果累积在寄存器或共享内存的 C 分块里。等 K 维全部走完,这个 C 分块才算完工,写回显存。

这个“沿 K 累加”的过程用公式看最清楚:C 的每个元素都是 A 行与 B 列在 K 维上的点积和。分块只是把求和顺序打散再重组,结果不变,但数据局部性大大提升——同一块数据被反复使用而不用反复读显存。

运砖要等砌墙,砌墙要等抹灰,如果工位间干等着,工地就停工了。聪明的施工队会让下一批料“提前备好”——这就是下一节的流水线和双缓冲。

Cij=∑k=1KAik⋅BkjC_{ij} = \sum_{k=1}^{K} A_{ik} \cdot B_{kj}
每个输出元素都是 A 的一行与 B 的一列做内积,K 维求和是所有 GEMM 优化的核心循环。
  1. 输出元素 Cij结果矩阵 C 第 i 行第 j 列的元素,由 A 的第 i 行与 B 的第 j 列共同决定,是分块后每块要计算的原子单元。
    CijC_{ij}
  2. A 的元素 AikA 矩阵第 i 行第 k 列,是乘数之一,对应从 A 分块里加载的条带元素。
    AikA_{ik}
  3. B 的元素 BkjB 矩阵第 k 行第 j 列,与 Aik 配对相乘,对应从 B 分块里加载的条带元素。
    BkjB_{kj}
  4. 沿 K 累加对 k 从 1 到 K 求和,对应 GEMM 主循环里每个 K 分块的乘累加,最终得到完整结果。
    ∑k=1KAik⋅Bkj\sum_{k=1}^{K} A_{ik} \cdot B_{kj}

分块与流水线:让工位转起来

工地最怕“人等料”。砌墙师傅刚砌完一块,砖还没送到,只能干等。资深工头会让送料组提前把下一块砖运到,砌墙组伸手就能拿——这就是双缓冲:一个缓冲区在“喂”计算,另一个已经装好下一块料。

cuTLASS 在共享内存里布置双缓冲甚至多级流水线:加载第 t+1 块数据的同时,Tensor Core 正在算第 t 块。数学上,每轮循环耗时不再是“加载时间 + 计算时间”,而是约等于两者中较大的那个——等待被隐藏在计算背后。

除了双缓冲,还有同步机制(barrier)保证“料没到不能开工、活没干完不能换料”。分块设计、流水线深度、共享内存大小之间互相牵制:流水线越深越能隐藏延迟,但占用的共享内存也越多,能容纳的并发块就越少。cuTLASS 把这些全部做成模板参数,交给开发者权衡。

流水线让工地“不停转”,但不同墙型适合不同工法:有的墙又高又薄,有的方形厚实。下一节看 cuTLASS 如何“看菜下饭”地挑选 kernel。

Tloop≈max⁡(Tload,Tcompute)T_{\text{loop}} \approx \max(T_{\text{load}}, T_{\text{compute}})
流水线的魔力:加载与计算重叠后,每轮耗时只取决于较慢的一方,而不是两者之和。
  1. 加载耗时 T_load从全局显存把一个分块搬进共享内存所需的时间,类似运砖的时间。
    TloadT_{\text{load}}
  2. 计算耗时 T_computeTensor Core 完成这个分块乘累加所需的时间,类似砌墙的时间。
    TcomputeT_{\text{compute}}
  3. 取最大值双缓冲让加载和计算重叠,慢的那一方决定本轮耗时,快的一方被隐藏。
    max⁡(Tload,Tcompute)\max(T_{\text{load}}, T_{\text{compute}})
  4. 近似总耗时每轮循环约等于二者中较大的一个,这就是流水线隐藏延迟的数学表达。
    Tloop≈max⁡(Tload,Tcompute)T_{\text{loop}} \approx \max(T_{\text{load}}, T_{\text{compute}})

Kernel 选择策略:看菜下饭

同样是盖墙,小院围墙手砌就行,摩天楼得用塔吊和预制板。矩阵乘法也一样:几百维的小矩阵和上万维的大矩阵,用同一套 kernel 必然是浪费。cuTLASS 提供多种 kernel 风格——SIMT 核(普通 CUDA 核)、Tensor Core 核、Split-K 核等,供不同场景挑选。

选择的依据之一是“算术强度”:每个搬运字节配多少次运算。算术强度高(K 很大)意味着计算密集,适合大分块加 Tensor Core;算术强度低(比如向量乘矩阵的 GEMV)瓶颈在带宽,用 SIMT 核加小分块反而更好。

cuTLASS 的启发式规则会根据 M、N、K 和 GPU 架构自动选 kernel:大矩阵用 Tensor Core 加大 Tile,小矩阵或批量小的 GEMV 用 SIMT 核避免“大炮打蚊子”;K 特别大时用 Split-K,把 K 维切成多段并行算再加总。开发者也可以绕过启发式,直接指定想要的 kernel。

“看菜下饭”选好 kernel 之后,大方向就定了。接下来是细枝末节的打磨——怎么把每个螺丝都拧到最合适的松紧度?这就是性能调优。

AI=2×M×N×KM×K+K×N+M×NAI = \frac{2 \times M \times N \times K}{M \times K + K \times N + M \times N}
算术强度衡量“每搬一个数据能算多少次”,是选大分块还是小分块、选 Tensor Core 还是 SIMT 核的关键指标。
  1. 分子 2MNKGEMM 要做 M×N 个输出元素,每个需要 K 次乘加,每次乘加算 2 次浮点运算,这就是总计算量。
    2×M×N×K2 \times M \times N \times K
  2. 分母数据量A、B、C 三个矩阵的元素总数之和,代表如果完全不复用,需要搬运的数据量。
    M×K+K×N+M×NM \times K + K \times N + M \times N
  3. 相除得强度比值越大说明每搬一个字节能做更多计算,越适合计算密集的 Tensor Core kernel;比值越小越需要省带宽。
    2×M×N×KM×K+K×N+M×N\frac{2 \times M \times N \times K}{M \times K + K \times N + M \times N}

性能调优:拧螺丝的艺术

选好了施工方案,工地开工了,但工头还会盯着数据较真:同样的墙,为什么我这队比隔壁慢 20%?于是开始调参数:分块尺寸、线程块大小、双缓冲深度、寄存器占用……每颗螺丝都拧到最合适的松紧度。

cuTLASS 的调优本质是在“数据复用”和“并发度”之间找平衡。分块越大,数据复用越多,但共享内存和寄存器被占满,并发块减少;分块越小,并发高,但每块搬数据的开销变大。实际调优通常用自动化参数扫描(sweep),在网格上跑几组配置,挑出 TFLOPS 最高的那一组。

一个经验:在 A100 上用 Tensor Core 跑 8192×8192 的大 GEMM,cuTLASS 可以达到 80%~90% 的峰值算力;而同样的库跑 32×32 的小矩阵,利用率可能只有个位数百分比。所以“调优收益”高度依赖问题规模——大矩阵拼效率,小矩阵拼策略。

回到工头视角:cuTLASS 的整套思路——构件仓库、规格单、钢印机、流水线、看菜下饭、拧螺丝——最终都指向同一个目标:让 GPU 上的 GEMM 又快又稳。理解了这些,你也能在需要时亲手为模型定制最合适的那面“墙”。

TFLOPS=2×M×N×Kt×1012\text{TFLOPS} = \frac{2 \times M \times N \times K}{t \times 10^{12}}
TFLOPS 是衡量 GEMM 性能的“工地速度表”:每秒完成多少万亿次浮点运算,越接近硬件峰值越好。
  1. 总运算次数GEMM 的全部浮点运算次数,乘和加各算一次所以乘以 2,与算术强度公式里的分子一致。
    2×M×N×K2 \times M \times N \times K
  2. 实测耗时 tkernel 从启动到完成经过的秒数,是性能的真实刻度。
    tt
  3. 换算为每秒万亿次乘以 10 的 12 次方把“每秒运算次数”换算成 TFLOPS 单位,方便与硬件标称峰值对比。
    t×1012t \times 10^{12}
  4. 性能数值分子分母相除,得到每秒多少万亿次浮点运算;除以硬件峰值就是利用率。
    2×M×N×Kt×1012\frac{2 \times M \times N \times K}{t \times 10^{12}}

AI 应用场景

大模型推理中的 GEMM 定制优化

阶段:模型推理与部署优化

解决的问题:通用推理框架里的矩阵乘 kernel 是“均码成衣”,对特定尺寸(如 LLM 解码阶段的 1×4096×4096 GEMV)利用率可能只有 20%。cuTLASS 让工程师像选预制板一样,为注意力打分和 FFN 层定制 GEMM 模板,把每块“板材”都用满。

具体案例:某大模型服务商用 TensorRT 部署 7B 对话模型时,发现解码阶段的小批量 GEMM 存在带宽瓶颈。团队基于 cuTLASS 的模板重写了 QKV 投影层,开启 Split-K 并把分块从 128 降到 64,首 token 延迟降低约 25%,吞吐提升 30%。

混合精度训练的自定义线性层

阶段:分布式训练加速

解决的问题:混合精度训练需要 FP16 计算、FP32 累加,同时要处理梯度缩放;默认框架只会用“通用现成墙”,缺乏针对特定形状的调优。

具体案例:训练 100B 参数模型时,数据并行加张量并行的 all-reduce 通信开销很大。团队用 cuTLASS 的混合精度 MMA 模板实现自定义 Linear 加残差连接,把计算时间压到通信时间之下,端到端训练效率提升 18%。

Python 演示

python60 行
import timeimport numpy as np  def naive_gemm(a, b):    # 三重循环的朴素矩阵乘法, 逐元素累加    m, k = a.shape    _, n = b.shape    c = np.zeros((m, n), dtype=np.float64)    for i in range(m):        for j in range(n):            acc = 0.0            for kk in range(k):                acc += a[i, kk] * b[kk, j]            c[i, j] = acc    return c  def tiled_gemm(a, b, tile=8):    # 分块累加的 GEMM, 模拟 cuTLASS 的分块计算结构    m, k = a.shape    _, n = b.shape    c = np.zeros((m, n), dtype=np.float64)    for i0 in range(0, m, tile):        for j0 in range(0, n, tile):            acc = np.zeros((tile, tile), dtype=np.float64)            for k0 in range(0, k, tile):                acc += a[i0:i0 + tile, k0:k0 + tile] @ b[k0:k0 + tile, j0:j0 + tile]            c[i0:i0 + tile, j0:j0 + tile] = acc    return c  def verify():    rng = np.random.default_rng(7)    a = rng.random((16, 16))    b = rng.random((16, 16))    ref = a @ b    assert np.allclose(naive_gemm(a, b), ref, atol=1e-8)    assert np.allclose(tiled_gemm(a, b, tile=4), ref, atol=1e-8)    assert np.allclose(tiled_gemm(a, b, tile=8), ref, atol=1e-8)    print("all three implementations match")  def benchmark():    rng = np.random.default_rng(0)    for n in (32, 48, 64):        a = rng.random((n, n))        b = rng.random((n, n))        t0 = time.perf_counter()        naive_gemm(a, b)        t1 = time.perf_counter()        tiled_gemm(a, b, tile=8)        t2 = time.perf_counter()        print("n=%3d naive=%8.2fms tiled=%8.2fms" %              (n, (t1 - t0) * 1000, (t2 - t1) * 1000))  if __name__ == "__main__":    verify()    benchmark()
目的:用 Python 模拟 cuTLASS 的分块 GEMM 结构——先验证分块与朴素实现结果一致,再对比两者在不同矩阵尺寸下的耗时,直观理解“分块只改顺序、不改结果”。
  1. def tiled_gemm(a, b, tile=8):定义分块 GEMM 的入口,tile 相当于 cuTLASS 的模板参数 TILE_SIZE。
  2. for i0 in range(0, m, tile):按行方向把 C 切成小砖,模拟线程块的横向分块。
  3. acc = np.zeros((tile, tile), dtype=np.float64)每个 C 分块的累加器清零,对应 Tensor Core 的累加寄存器。
  4. acc += a[i0:i0 + tile, k0:k0 + tile] @ b[k0:k0 + tile, j0:j0 + tile]K 维循环里逐个 K 分块乘累加,就是 GEMM 主循环的“接力”。
  5. c[i0:i0 + tile, j0:j0 + tile] = accK 维全部走完后把累加结果写回 C 分块,对应“写回显存”。
  6. assert np.allclose(tiled_gemm(a, b, tile=4), ref, atol=1e-8)用 numpy 参考结果验证分块顺序不影响正确性。

常见误区

误区听说 cuTLASS 性能和灵活性都更好,我打算把所有 cuBLAS 调用全部换成 cuTLASS。

正解两者是“成品墙”和“构件仓库”的区别。cuBLAS 黑盒封装、即调即用;cuTLASS 需要自己实例化模板、管理调度和共享内存,调优成本高。只有对性能敏感的算子才值得用 cuTLASS,通用场景直接上 cuBLAS 更快更稳。

为何常见:两者都做 GEMM,名字又相似,很容易误认为后者全面取代前者。

误区只要 GPU 有 Tensor Core,任何矩阵乘法都会自动变快。

正解Tensor Core 的 MMA 指令要求特定形状(如 16×16×16)和特定精度(FP16/BF16/INT8)。小矩阵或行向量 GEMV 时,数据搬移占主导,Tensor Core 几乎帮不上忙。cuTLASS 里的 SIMT 内核正是服务于这类场景。

为何常见:厂商宣传总拿大 GEMM 的加速倍数说事,让人忽略形状和精度的硬性条件。

误区我把分块、流水线深度、线程数全部暴露成模板参数,调优空间最大。

正解参数过多会带来组合爆炸——编译时间暴涨、二进制体积膨胀、选择困难。cuTLASS 采用“合理默认值加按需覆盖”的设计,就像规格单只填关键几栏,其他用默认。

为何常见:工程师容易把“可配置”等同于“越多越好”,忽视了工程成本。

误区共享内存里放最大的块,K 维一次全加载,数据复用最充分,性能一定最高。

正解分块过大会占用过多共享内存和寄存器,降低 block 占用率,流水线无法交错,反而变慢。cuTLASS 的调优正是在“复用”和“并发”之间找平衡,而不是单点最优。

为何常见:“多即是好”的直觉,忽略了硬件资源是有限的。

误区它是配合 Tensor Core 的 AI 专用库,科学计算用不上。

正解cuTLASS 是通用线性代数模板库,覆盖 SGEMM/DGEMM/HGEMM 等多种精度,广泛用于 HPC 模拟、物理场计算、数据库矩阵运算等。Tensor Core 只是它支持的一类加速指令。

为何常见:Tensor Core 常与 AI 绑定宣传,让人误以为整个库都只服务深度学习。

面试题

Q1. cuTLASS 与 cuBLAS 的最大区别是什么?

单选题 · easy

  • A.提供完全相同的接口
  • B.开放源码且高度模板化← 正确答案
  • C.只能运行在旧代 GPU
  • D.不支持混合精度运算

答案:B。cuBLAS 是闭源黑盒,即调即用;cuTLASS 是开源模板库,像预制构件仓库一样让开发者自定义 GEMM 的每个环节。A、C、D 都与事实不符。

Q2. Tensor Core 的 MMA 指令一次完成什么操作?

单选题 · easy

  • A.一次完成小矩阵乘加← 正确答案
  • B.一次完成标量乘加
  • C.一次完成整块内存拷贝
  • D.一次完成全矩阵转置

答案:A。MMA(矩阵乘累加)是“一体化钢印机”,一条指令完成 D = A×B + C;标量乘加是普通 CUDA 核心做的事,转置和拷贝不是 MMA 的职责。

Q3. GEMM 公式 C = αAB + βC 中 β 的作用是什么?

单选题 · easy

  • A.控制 A 矩阵的大小
  • B.保留并缩放原有 C← 正确答案
  • C.调整 B 矩阵的精度
  • D.指定分块的数量

答案:B。β 决定旧 C 保留多少,实现“累加写回”而非覆盖——这正是 GEMM 中最后一个“加”的含义,也是多次调用叠加结果的基础。

Q4. 以下哪个是 cuTLASS 的典型模板参数?

单选题 · mid

  • A.分块尺寸与数据类型← 正确答案
  • B.显存容量与带宽
  • C.风扇转速与温度
  • D.操作系统与驱动版本

答案:A。TILE_M/TILE_N/TILE_K 和数据精度都由模板参数在编译期确定;显存、风扇、操作系统属于运行环境,不是编译期模板参数。

Q5. K 维分块循环中,每个输出分块的正确做法是什么?

单选题 · mid

  • A.不断乘积累加← 正确答案
  • B.每次覆盖清零
  • C.随机挑选一个子块
  • D.直接转置输出

答案:A。沿 K 维循环时,每个小分块乘完要累加到同一个 C 分块上,对应公式 ΣA·B;清零、随机选块和转置都会破坏累加语义。

Q6. 双缓冲技术主要用来隐藏什么开销?

单选题 · mid

  • A.浮点运算的开销
  • B.显存加载的延迟← 正确答案
  • C.线程创建的开销
  • D.编译器优化的时间

答案:B。双缓冲让“下一块数据加载”与“当前块计算”重叠,把对显存的等待藏到计算背后,这就是流水线隐藏延迟的基本功。

Q7. 分块过大导致性能下降的主要原因是什么?

单选题 · hard

  • A.占用过高降低占用率← 正确答案
  • B.矩阵无法正确相乘
  • C.编译器无法生成代码
  • D.精度下降为半精度

答案:A。分块越大占共享内存和寄存器越多,并发 block 数下降,流水线交错度变差,最终变慢。正确性不受影响,精度与分块尺寸无关。

Q8. Tensor Core 加速 GEMM 需要满足哪些条件?

多选题 · mid

  • A.形状符合 MMA 规格← 正确答案
  • B.精度受指令集支持← 正确答案
  • C.必须使用双精度浮点
  • D.GPU 包含张量核心← 正确答案

答案:ABD。MMA 对形状(如 16×16×16)和精度(FP16/INT8 等)有硬性要求;双精度通常走 CUDA 核心而非 Tensor Core;没有张量核心的 GPU 自然无法使用该指令。

Q9. cuTLASS 高性能的来源包括哪些?

多选题 · mid

  • A.编译期模板特化← 正确答案
  • B.共享内存数据复用← 正确答案
  • C.流水线延迟隐藏← 正确答案
  • D.运行时动态解释执行

答案:ABC。编译期特化消除运行时分支,共享内存分块提升复用率,多级流水线重叠隐藏延迟;运行时动态解释会拖慢性能,与高性能目标相反。

Q10. 选择最优 GEMM kernel 时需要考虑哪些因素?

多选题 · hard

  • A.矩阵尺寸与形状← 正确答案
  • B.GPU 架构的代际← 正确答案
  • C.数据的内存布局← 正确答案
  • D.操作系统的版本号

答案:ABC。尺寸决定用 SIMT 还是 Tensor Core kernel;架构代际决定支持哪些 MMA 形状和算力;内存布局(行主/列主)影响访问效率;操作系统与 CUDA kernel 的选择基本无关。