想象你走进一家开在繁华商圈的大型连锁餐厅。总部的设计图纸上,每一层楼都被规划成几十个一模一样的小后厨:同样的灶台数量、同样的橱柜、同样的排风扇,配置完全一致。为什么要这样设计?因为与其费尽心思造一个能同时炒一百道菜的巨型灶台,不如复制一大堆普通厨房让它们同时开工——分工协作,总吞吐量反而更高。GPU芯片的设计者正是这么想的:芯片里没有“一个超级处理器”,而是集成了一群结构相同、各自独立运转的流多处理器(Streaming Multiprocessor,简称SM)。 每个后厨里还有更精妙的安排:几位全能厨师什么菜都能炒,一台自动包饺子机专攻饺子,各位厨师的围裙口袋里别着常用的小工具,正中央的大理石备菜台供全组共用,冷藏仓库则在地下室。主厨把订单按32桌一组合并成团,哪组食材先送到就先做哪组,某组还在等电梯运菜时,主厨早已转身去炒另一组菜——绝不让灶台闲着。这套“分组下单、轮换开火”的机制,正是GPU能同时服务上万桌“客人”(线程)的秘密。 这篇文章会带你逛遍SM这个“神奇后厨”:认识灶台(CUDA核心)、包饺子机(张量核心)、口袋与备菜台(寄存器与共享内存),看懂主厨的排班表(Warp调度),再聊聊如何让后厨火力全开(性能优化)。

岗位关联

理解SM架构是GPU编程和深度学习性能调优的“内功心法”:无论是手写CUDA Kernel、编写Triton算子,还是为训练集群挑选显卡,都需要知道算力从哪里来、瓶颈在哪里。

目录
  1. 可视化演示
  2. SM基础架构
  3. 计算核心单元
  4. 存储层次
  5. Warp调度机制
  6. 任务组织模型
  7. 深度学习优化
  8. AI 应用场景
  9. Python 演示
  10. 常见误区
  11. 面试题

可视化演示

展示GPU SM内部的CUDA Core、Tensor Core协同与Warp调度机制

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

SM内部计算资源布局 每个SM包含64个CUDA Core和4个Tensor Core 寄存器文件与共享内存构成片上存储层次 SM = CUDA Cores + Tensor Cores + 存储 SM是GPU并行计算的基本执行单元

SM基础架构

想象一家开在繁华商圈的大型连锁餐厅。总部的设计图纸上,每一层楼都被规划成几十个一模一样的小后厨:同样的灶台数量、同样的橱柜、同样的排风扇,配置完全一致。为什么不像单户餐厅那样只做一个气派的大厨房?因为与其费尽心思造一个能同时炒一百道菜的巨型灶台,不如复制一大堆普通厨房让它们同时开工——分工协作,总吞吐量反而更高、也更可靠。

GPU芯片的设计者正是这么想的。GPU内部并没有“一个超级处理器”,而是集成了几十乃至上百个结构相同、相互独立的计算单元块,每个单元块都能独立取指令、独立做调度、独立执行计算。这些单元块就是流多处理器SM。打开一张Ampere架构的芯片白皮书,你会看到占据芯片绝大部分面积的不是某一个庞然大物,而是一排排整齐的SM方块,它们通过片上的互联网络与L2缓存、显存控制器相连。

一个典型的SM内部包含四类关键部件:第一,CUDA核心阵列——几十到上百个能执行整数、浮点运算的通用计算单元;第二,张量核心——专门加速矩阵乘法的硬件单元;第三,存储体系——寄存器文件、共享内存以及各级缓存;第四,控制逻辑——warp调度器和指令分发单元。从Fermi到Ampere再到Hopper,SM内部的布局不断调整,但“多模块协同、各司其职”的设计思想始终没变。GPU的并行度,本质上是SM数量的线性放大:

就像餐厅的繁荣靠的不只是单个厨师的厨艺,更是后厨的复制与排布,GPU的算力也深深扎根于SM的架构设计。理解了这个“后厨单元”,接下来我们就推门进去,看看里面的灶台和师傅们。

GPU总核心数=SM数量×每SM的CUDA核心数\displaystyle \text{GPU总核心数} = \text{SM数量} \times \text{每SM的CUDA核心数}
简单说:想知道一块显卡的“厨师总数”,就用后厨数量乘以每个后厨的厨师人数。比如A100有108个SM、每SM有128个CUDA核心,总核心数就是 13824。
  1. SM数量一块GPU芯片上流多处理器的个数,直接决定并行规模。A100拥有108个SM,是总核心数的“乘数”。
    SM数量=108\text{SM数量} = 108
  2. 每SM的CUDA核心数单个SM内标量运算单元的个数,A100每个SM内嵌128个。
    每SM的CUDA核心数=128\text{每SM的CUDA核心数} = 128
  3. 相乘求总把两者乘起来,得到整块GPU的CUDA核心总数,A100即为 13824。
    108×128=13824108 \times 128 = 13824

计算核心单元

每个后厨里都配有两种“人力”:全能厨师什么菜都能炒,从切丝到爆炒样样在行;自动包饺子机则只干一件事——包饺子,但速度是厨师的几十倍。SM里恰好也有这样两类核心,它们各司其职,配合起来威力惊人。

第一类是我们熟知的CUDA核心,它的本质是一个通用的标量运算单元,负责执行整数和浮点数的加减乘除、逻辑判断、比较以及融合乘加(FMA)等指令。所谓FMA,就是“乘法和加法一步完成”:计算 a = b × c + d 时,普通流程先乘后加两步,FMA用一个单元一口气算完。CUDA核心的优势在于“全能”——任何指令都能处理,哪怕遇到分支跳转也能灵活应对,因此适合控制流复杂、计算形态不规则的代码。

第二类是张量核心(Tensor Core),它是NVIDIA从Volta架构起为深度学习量身定制的“包饺子机”。一个张量核心每个时钟周期就能完成一个4×4×4矩阵的乘加运算,相当于4096次乘加。为什么专门造这种单元?因为神经网络里最耗时的操作——卷积、全连接、注意力——归根结底都是巨型矩阵乘法。把矩阵乘扔给张量核心,把元素级的辅助运算留给CUDA核心,这座后厨的产出效率就能成倍提升:

训练大模型时,绝大部分矩阵乘法都会被调度到“包饺子机”上完成,全能厨师则负责激活函数、归一化、数据搬移等杂活。两类核心的无间配合,才是SM能撑起千亿参数训练的关键。

峰值算力(TFLOPS)=SM数×每SM张量核心数×频率×单核每周期乘加数×21012\displaystyle \text{峰值算力(TFLOPS)} = \frac{\text{SM数} \times \text{每SM张量核心数} \times \text{频率} \times \text{单核每周期乘加数} \times 2}{10^{12}}
公式的意思是:算力的“总盘子”由张量核心的个数、工作频率和每个核心的干活速率共同决定。最后乘2,是因为一次乘加运算要算“乘法”和“加法”两次浮点操作。
  1. SM数与每SM张量核心数刻画张量核心的硬件规模,A100每个SM有4个张量核心,全卡共432个。
    SM数×每SM张量核心数\text{SM数} \times \text{每SM张量核心数}
  2. 频率核心的工作时钟频率,单位是Hz,决定每秒钟能执行多少个周期。
    频率≈1.41×109 Hz\text{频率} \approx 1.41 \times 10^{9}\ \text{Hz}
  3. 单核每周期乘加数一个张量核心每个周期能完成的乘加操作对数,Ampere为1024次。
    单核每周期乘加数=1024\text{单核每周期乘加数} = 1024
  4. 乘2换算FLOPs一次乘加包含一次乘法和一次加法,按浮点运算次数计为2次。
    FLOPs=乘加数×2\text{FLOPs} = \text{乘加数} \times 2
  5. 除以10¹²得TFLOPS把每秒万亿次浮点运算作为单位,即最终宣传的峰值算力。
    TFLOPS=FLOPs1012\text{TFLOPS} = \dfrac{\text{FLOPs}}{10^{12}}

存储层次

厨师的工作效率不仅取决于手艺,更取决于“取料”的速度。最贴身的是围裙口袋里随时能摸到的盐瓶,取用只需一眨眼;稍远一点是后厨正中央的大理石备菜台,大家手边的半成品都码在上面;最远的则是地下室的冷藏仓库,容量大得惊人,但跑一个来回要好几分钟。SM的存储设计,完全复刻了这套“贴着算、就近放、远处存”的哲学。

最贴身的是寄存器文件(Register File)。它紧贴着CUDA核心,每个线程都有自己私有的几个寄存器,访问延迟只有约1个时钟周期,是所有存储中速度最快的。但寄存器是稀缺资源——A100每个SM的寄存器文件只有256KB,平均下来每个线程大约只能分到几十个寄存器。接下来是共享内存(Shared Memory),它同样位于SM内部,但属于同一个线程块的所有线程共用,大家可以通过它交换数据、协作计算,访问延迟约20~30个周期。共享内存需要程序员在代码里显式声明和搬运数据,相当于“备菜台要自己摆盘”。最外围则是全局内存(显存),容量可达数十GB,但延迟高达400~800个时钟周期——去仓库取一趟菜的时间,够灶台炒几十道菜了。SM内共享内存的带宽估算公式如下:

在CUDA优化中,数据放对位置比什么都重要。把反复用到的数据从仓库搬上备菜台(共享内存),把最热的数据塞进口袋(寄存器),让访问全局内存的次数降到最低——这套“就近取料”的思路,正是FlashAttention等经典算子的核心设计逻辑。

共享内存总带宽=时钟频率×数据位宽×SM数量\displaystyle \text{共享内存总带宽} = \text{时钟频率} \times \text{数据位宽} \times \text{SM数量}
共享内存是SM上的“高速备菜台”:位宽越宽、频率越高、后厨数量越多,整块GPU从备菜台取菜的总速度就越快,远高于从仓库(显存)取菜的速度。
  1. 时钟频率SM的工作频率,决定共享内存每秒钟能完成多少次读写事务。
    时钟频率≈1.41×109 Hz\text{时钟频率} \approx 1.41 \times 10^{9}\ \text{Hz}
  2. 数据位宽每个时钟周期能从共享内存读出(或写入)的字节数,A100每个SM为128字节。
    数据位宽=128 Bytes\text{数据位宽} = 128\ \text{Bytes}
  3. SM数量每个SM的共享内存独立并行工作,总带宽随SM数线性叠加。
    总带宽=单SM带宽×SM数\text{总带宽} = \text{单SM带宽} \times \text{SM数}

Warp调度机制

主厨不会一盘菜一盘菜地炒,而是把订单按32桌合成一组,整组一起下锅。哪一组食材先到就先做哪一组;某组还在等配菜,主厨就转身去炒别的组。后厨里最忌讳的事是“人等灶”——订单可以等,灶台绝不能闲着。GPU里的调度,玩的就是同一套把戏。

Warp是GPU硬件的最小调度单位:32个线程被捆绑为一组,始终执行同一条指令,这就是SIMT(单指令多线程)模型。当你在CUDA里启动一个包含1024个线程的线程块时,硬件会把它自动切成32个warp,逐个派发执行。每个SM内部有4个独立的warp调度器,每个调度器每个时钟周期可以从“就绪”的warp中挑选一个,向计算核心发射指令。这里的关键在于延迟隐藏:全局内存访问动辄几百个周期的延迟,但这期间调度器并不等待,而是立刻切换到另一个不依赖该数据的warp继续执行——“人歇灶不歇”。SM能同时容纳的warp数量有限(如A100为64个),实际驻留的warp数与上限的比值,就是GPU圈子里常说的占用率:

只要排队等菜的人足够多,主厨就永远有活干;同理,只要驻留的warp足够多,计算单元就不会饿肚子。这也是为什么CUDA优化中,经常用“提高占用率”来对抗访存延迟——不是为了多算,而是为了“总有活可干”。

占用率=活跃Warp数SM支持的最大Warp数×100%\displaystyle \text{占用率} = \frac{\text{活跃Warp数}}{\text{SM支持的最大Warp数}} \times 100\%
占用率的直观理解:候餐区的排队人数。排队的人越多,主厨越不容易“等菜等得没活干”;但排队的坑位(寄存器、共享内存)是有限的,想多排一桌,就得让每桌占用的资源更少。
  1. 活跃Warp数当前已驻留在SM上、能够被调度器挑选执行的warp数量。
    活跃Warp数=驻留线程数÷32\text{活跃Warp数} = \text{驻留线程数} \div 32
  2. 最大Warp数硬件规定的上限,由线程容量、寄存器用量、共享内存占用三者共同约束。
    最大Warp数=64 (A100)\text{最大Warp数} = 64\ (\text{A100})
  3. 乘以100%把比值转换成百分数,便于对比不同配置下的调度潜力。
    占用率=活跃Warp数64×100%\text{占用率} = \dfrac{\text{活跃Warp数}}{64} \times 100\%

任务组织模型

餐厅总部接到一个两千人的婚宴订单,当然不会把活儿全压给某一个后厨。销售部会把宴席拆成每桌8~10人的小桌,每张桌子的菜单装进一个信封,再按后厨的忙闲程度分发给各楼层的厨房。CUDA把任务派发给SM的方式,和这套流程如出一辙。

程序员在CUDA中启动内核时,会定义一个“网格”(Grid),网格是“线程块”(Block)的集合;每个线程块包含若干线程,比如128、256或512个。硬件拿到这个网格后,会把一个个线程块按顺序分配给各个SM——每个线程块一旦被分配到某个SM,就会“定居”下来,直到所有线程执行完毕才会腾出位置。线程块内部,线程之间可以通过共享内存交换数据,并借助 __syncthreads() 设置“集合点”:块内所有线程必须都到达这个点,才能继续往下走,就像一桌客人必须全部到齐才能开席。而线程块之间完全独立,互不感知。每个SM能同时驻留多少个线程块,由硬件容量和块大小共同决定:

把订单切成合适大小的“桌”,才能让每个后厨都恰好满负荷,又不至于让一桌客人跨两个厨房。实际工程里,block大小选128还是512,直接影响warp的划分、共享内存的占用和最终的负载均衡——这正是每一位CUDA开发者反复调参的核心原因。

每SM驻留线程块数=⌊SM线程容量线程块大小⌋\displaystyle \text{每SM驻留线程块数} = \left\lfloor \frac{\text{SM线程容量}}{\text{线程块大小}} \right\rfloor
可以理解为:一个大宴会厅能坐2048人,每张桌坐256人,那最多能摆8张桌。桌子不能切开跨厅摆放,所以除不尽的余数座位只能空着。
  1. SM线程容量SM能同时容纳的最大线程总数,A100为2048个线程。
    SM线程容量=2048\text{SM线程容量} = 2048
  2. 线程块大小每个block包含的线程数量,由程序员在启动内核时设定。
    线程块大小=256\text{线程块大小} = 256
  3. 向下取整线程块不能跨越SM,多出来的碎片容量无法被利用,因此向下取整。
    ⌊2048256⌋=8\left\lfloor \frac{2048}{256} \right\rfloor = 8

深度学习优化

想让后厨的产出翻倍,聪明的老板不会只给厨师发更多调料,而是重新设计流程:包饺子不用手工改走机器,配菜提前切好码上备菜台,采购一趟就把整周的食材拉回来。深度学习在GPU上的性能优化,本质上就是这三板斧。

第一板斧:把矩阵乘法交给张量核心,并用“分块”(Tiling)策略让数据在共享内存里反复复用。以经典的FlashAttention为例,它不再把巨大的注意力矩阵整体算完再写回显存,而是把Q、K、V切成一块块小片,轮流搬进共享内存,在SM内部完成分块计算后再写回——访存量从O(N²)级别降到了O(N)级别,训练速度成倍提升。第二板斧:让访存“合并”。一个warp里的32个线程如果读写连续的地址,硬件可以一次事务把所有数据取完;如果各自访问分散的地址,就要拆成几十次事务,效率暴跌。第三板斧:用“算术强度”判断该优化什么。这个指标衡量“每读一个字节的数据,能做多少次浮点运算”:

TensorRT、FlashAttention、cuBLAS这些名字之所以如雷贯耳,正是因为它们在帮你的“后厨”重新设计流程:让包饺子机满转、备菜台高效周转、仓库访问次数压到最低。理解了SM的内部结构,你就能看懂这些优化工具的每一招,甚至自己写出下一个FlashAttention。

算术强度=浮点运算量(FLOPs)访存量(Bytes)\displaystyle \text{算术强度} = \frac{\text{浮点运算量(FLOPs)}}{\text{访存量(Bytes)}}
如果算术强度低,说明数据搬来搬去却算不了几笔账——瓶颈在“取料”(访存),优先增加数据复用;如果算术强度高,说明计算都快赶不上搬数据了——瓶颈在“做菜”(计算),优先优化指令和核函数。
  1. 浮点运算量完成算法所需的全部浮点运算次数,一次乘加计2次FLOPs。
    FLOPs=乘加次数×2\text{FLOPs} = \text{乘加次数} \times 2
  2. 访存量从全局内存读取和写回的字节总数,单位是Bytes。
    访存量=读入+写出\text{访存量} = \text{读入} + \text{写出}
  3. 求比值两者相除得到算术强度,单位为 FLOPs/Byte,用于判断kernel属于访存受限型还是计算受限型。
    算术强度=FLOPsBytes\text{算术强度} = \frac{\text{FLOPs}}{\text{Bytes}}

AI 应用场景

大模型训练中的矩阵乘法加速

阶段:模型训练(算力核心)

解决的问题:大模型训练中线性层、注意力层反复执行巨型矩阵乘法,若用通用单元手工计算,速度慢几个数量级,训练周期从天级拖到月级。AI如何解决:将矩阵乘运算调度到SM内的张量核心,配合混合精度(FP16/BF16),让“包饺子机”全速运转。

具体案例:训练LLaMA-2 70B时,前向和反向传播中超过90%的浮点运算集中在矩阵乘法。通过cuBLAS库将GEMM算子映射到张量核心,并按SM的共享内存容量做分块(如128×128的tile),在A100上实现约312 TFLOPS的FP16峰值算力,训练吞吐相比纯CUDA核心方案提升5~10倍。

FlashAttention的共享内存分块

阶段:算子开发与推理优化

解决的问题:标准注意力机制需要计算N×N的注意力矩阵并写回显存,当序列长度达到数千甚至数万时,访存开销呈平方级爆炸,GPU利用率极低。AI如何解决:把Q/K/V切成小块,利用SM的共享内存做“备菜台”,在SM内完成分块注意力计算,避免大矩阵落盘。

具体案例:在长文本推理场景中,BLOOM-176B的生成速度受限于注意力层的访存瓶颈。引入FlashAttention-2后,每个线程块把查询块加载到共享内存,循环读取键值块并在寄存器中累积结果,显存访问量从O(N²)降至O(N),单卡推理吞吐提升约2~3倍。

推理卡选型与SM利用率评估

阶段:模型部署与硬件选型

解决的问题:部署团队常遇到“买的卡不便宜,但GPU利用率只有20%”的困惑——显存占用高不代表算力被用起来。AI如何解决:通过NVIDIA Nsight Compute等工具从SM视角做性能剖析,查看sm busy率、warp占用率、warp stall原因,定位瓶颈是访存受限还是计算受限。

具体案例:某团队部署Stable Diffusion时发现图像生成慢,profile结果显示SM的活跃warp数远低于上限,根因是每个线程块占用的共享内存过大。把block size从512调到256、精简共享内存申请后,占用率从35%升到70%,端到端推理延迟下降45%。

Python 演示

python67 行
"""SM集群调度模拟:演示Warp轮转调度与延迟隐藏"""from collections import deque MAX_WARPS = 6  # SM 最多容纳的活跃 Warp 数  class Warp:    """一个 Warp 代表 32 个线程构成的指令流"""     def __init__(self, wid, inst_count=6):        self.wid = wid        self.inst_left = inst_count        self.ready_at = 0  # 本次访存返回的全局周期     def issue(self, cycle):        """执行一条指令,返回 True 表示整个 Warp 已完成"""        self.inst_left -= 1        if self.inst_left % 2 == 1:  # 每 2 条指令模拟一次访存等待            self.ready_at = cycle + 3        return self.inst_left <= 0  class SM:    """流多处理器:单个调度器对活跃 Warp 做轮转调度"""     def __init__(self):        self.active = deque()        self.done = []     def add(self, warp):        if len(self.active) < MAX_WARPS:            self.active.append(warp)            return True        return False     def tick(self, cycle):        if not self.active:            return        warp = self.active.popleft()        if warp.ready_at > cycle:            self.active.append(warp)  # 访存未返回,先切到别的 Warp            return        if warp.issue(cycle):            self.done.append(warp.wid)        else:            self.active.append(warp)  def main():    sm = SM()    total = 12    index = 0    cycle = 0    while index < total or sm.active:        while len(sm.active) < MAX_WARPS and index < total:            sm.add(Warp(index))            index += 1        sm.tick(cycle)        if len(sm.done) == total:            break        cycle += 1    print(f"共调度 {total} 个 Warp(每个含 32 线程),耗时 {cycle} 周期")    print("完成顺序:", sm.done)  if __name__ == "__main__":    main()
目的:用一个极简队列模型模拟SM的warp调度器,演示“访存等待时切换其他warp”的延迟隐藏机制。
  1. self.active = deque()活跃Warp队列,对应SM调度器手里的“待排班表”。
  2. if warp.ready_at > cycle:判断访存是否已返回,未返回就放回队尾,先执行别的Warp。
  3. while len(sm.active) < MAX_WARPS and index < total:队列有空位就补充新Warp,对应线程块分批入驻SM的过程。
  4. sm.tick(cycle)每个时钟周期调度器执行一次派发,对应硬件每周期发射一条指令。
  5. print(f"共调度 {total} 个 Warp...")输出总耗时,直观感受延迟隐藏对吞吐的贡献。

常见误区

误区感觉GPU就是无数个小处理器粗暴地拼在一起,SM只是机构造上的筐,随便堆多少个核心性能就翻多少倍。

正解SM远不止“装核心的筐”。它自带独立的warp调度器、寄存器文件、共享内存和指令分发单元,是一个能够自主取指、调度、执行的完整微处理器。GPU的可扩展性确实来自SM数量的增加,但每个SM的内部组织方式——比如调度带宽、存储带宽、张量核心配比——直接影响单SM的效率和深度学习负载的表现。A100与RTX 3090的SM数量相近时,训练性能也会因张量核心和存储配置差异而天差地别。

为何常见:厂商宣传页上“几千个核心”的数字太显眼,让人误以为并行度只等于核心数量,忽略了“核心是如何被组织起来的”。

误区以为共享内存和L1缓存是同一个东西,数据放进去后硬件会自动帮你加载和置换,程序员什么都不用管。

正解共享内存是程序员显式管理的暂存区(Scratchpad),数据必须由代码用 __shared__ 声明、手动从全局内存搬入搬出;L1缓存则是硬件自动管理的缓存,程序员无法直接控制其内容。两者在物理上都位于SM内、延迟接近,但一个“手动挡”一个“自动挡”。写CUDA Kernel时用错这两者,轻则性能平平,重则数据出错。

为何常见:两者都在SM内部、访问延迟都很低,教科书上又常常把它们画在同一个框图里,容易让人误以为是同一件东西。

误区以为写CUDA时要手动把线程凑成32个一组,像管理进程池一样自己去分组、去调度。

正解Warp是硬件层面的调度单位,程序员在代码里只关心线程和线程块。硬件会把线程块自动切成若干32线程的warp,一切分组、派发、切换都由SM内的调度器完成。程序员能做的只是“间接影响”——比如通过控制线程块大小、避免分支发散,来影响warp的划分和执行效率。

为何常见:CUDA API里存在 __shfl_sync、__syncthreads 这类与warp相关的函数,让人误以为warp是需要手动管理的一等公民。

误区觉得寄存器是“私有的高速缓存”,每个线程分到的寄存器越多,计算就越快,所以写代码时拼命声明变量。

正解寄存器文件总量是固定的(如A100每SM 256KB),每个线程多占一个寄存器,SM能同时驻留的线程就越少,warp数下降,占用率随之降低,延迟隐藏能力反而变差。专业做法是用 __launch_bounds__ 或 -maxrregcount 限制寄存器用量,在“每线程寄存器数”和“驻留warp数”之间寻找最佳平衡点。

为何常见:沿用了CPU优化的直觉——缓存越大越好、私有空间越多越好,没有意识到GPU的并行吞吐比单线程的“舒适度”更值钱。

面试题

Q1. 流多处理器(SM)在GPU中的角色是?

单选题 · easy

  • A.负责GPU与CPU通信的接口
  • B.独立完成计算的核心单元← 正确答案
  • C.管理显存分配的硬件单元
  • D.连接多个GPU的高速总线

答案:B。SM是GPU内部可独立取指、调度和执行线程的计算核心单元,GPU的并行能力正是由众多SM叠加而成。A是NVLink/PCIe的工作,C通常由显存控制器承担,D则是互联总线的职责,都与SM的定义无关。

Q2. 一个Warp包含多少个线程?

单选题 · easy

  • A.8个线程组成一个Warp
  • B.16个线程组成一个Warp
  • C.32个线程组成一个Warp← 正确答案
  • D.64个线程组成一个Warp

答案:C。Warp是GPU硬件调度的最小单位,固定包含32个线程,它们在同一时刻执行同一条指令(SIMT模型)。8、16、64都不是硬件规定的warp宽度,选其他数字说明对SIMT的基本模型记忆有误。

Q3. 张量核心的主要职责是?

单选题 · easy

  • A.加速矩阵乘加运算← 正确答案
  • B.执行分支跳转指令
  • C.管理共享内存读写
  • D.负责线程块负载均衡

答案:A。张量核心是为深度学习定制的矩阵乘加单元,单周期即可完成4×4×4矩阵乘加。分支跳转是CUDA核心的职责,共享内存读写和负载均衡分别由存储控制器、调度器负责,都不属于张量核心的专长。

Q4. SM中寄存器文件的主要特征是?

单选题 · mid

  • A.所有线程共享的大容量缓存
  • B.每个线程私有且访问最快← 正确答案
  • C.容量与全局内存相当
  • D.读写数据需要经过总线仲裁

答案:B。寄存器文件紧贴计算核心,每个线程拥有自己私有的寄存器,访问延迟仅1个周期,是SM中速度最快的存储。它并非线程共享,容量也只有几百KB,远小于显存,更无需总线仲裁——这些特征都指向“私有且最快”。

Q5. 共享内存与全局内存的核心区别是?

单选题 · mid

  • A.共享内存容量远大于全局内存
  • B.全局内存位于SM内部
  • C.共享内存位于SM内且延迟低← 正确答案
  • D.共享内存仅能存储常量数据

答案:C。共享内存在SM内部,延迟20~30周期,由块内线程共用;全局内存在芯片外部(显存),延迟400~800周期。共享内存容量远小于全局内存,而全局内存在芯片外而非SM内,共享内存也并非只能存常量,因此只能选C。

Q6. 延迟隐藏的核心机制是?

单选题 · mid

  • A.提高GPU主频加速所有指令
  • B.warp等待数据时切换其他warp← 正确答案
  • C.将数据全部预加载到显存
  • D.减少每个warp的指令数量

答案:B。延迟隐藏是指当一个warp因访存发生等待时,调度器立刻换入其他就绪warp执行,让计算单元始终忙碌。提高主频治标不治本,预加载到显存并不能消除访问延迟,减少指令数量与隐藏延迟更是无关。

Q7. 某SM最大支持2048个线程,每个线程块设置256个线程,该SM最多驻留几个线程块?

单选题 · hard

  • A.最多驻留2个线程块
  • B.最多驻留4个线程块
  • C.最多驻留8个线程块← 正确答案
  • D.最多驻留16个线程块

答案:C。线程块不能跨越SM,驻留块数 = SM线程容量 ÷ 线程块大小 = 2048 ÷ 256 = 8,因此最多驻留8个线程块。2和4低估了容量,16则超过了SM的线程上限,除非线程块大小是128才行。

Q8. SM内部的组成包括哪些核心部件?

多选题 · mid

  • A.CUDA核心← 正确答案
  • B.张量核心← 正确答案
  • C.光刻机单元
  • D.寄存器文件← 正确答案

答案:ABD。SM由CUDA核心阵列、张量核心、寄存器文件、共享内存以及warp调度器等部件构成。光刻机属于芯片制造设备,与SM的内部组成毫无关系,属于干扰项。

Q9. 影响SM占用率(Occupancy)的因素有哪些?

多选题 · mid

  • A.每个线程的寄存器用量← 正确答案
  • B.线程块占用的共享内存← 正确答案
  • C.GPU的显存总容量
  • D.线程块的大小设置← 正确答案

答案:ABD。占用率取决于SM中能驻留多少个warp,而驻留数量受每线程寄存器用量、每块共享内存占用以及线程块大小共同约束。显存总容量属于全局内存层面,与SM内部能塞下多少warp无关。

Q10. 关于Warp调度,以下说法正确的有?

多选题 · hard

  • A.一个warp内线程执行同一条指令← 正确答案
  • B.分支发散会降低warp执行效率← 正确答案
  • C.warp数量越多占用率一定越高
  • D.调度器可切换warp来隐藏延迟← 正确答案

答案:ABD。SIMT模型下一个warp的32个线程共享同一条指令;如果线程走向不同分支,warp会串行执行各分支,效率明显下降,所以应尽量避免分支发散;调度器在warp访存等待时切换其他warp正是延迟隐藏。C的错误在于warp数量受硬件上限约束,超过上限后增加warp不再提升占用率。