2012 年,你买了一台 Android 4.0 的手机,后来朋友换了 Android 13。你会发现,很多新 App 在旧手机上装不了,新功能(比如桌面小组件、后台分屏)也完全用不上——手机硬件没变,但“系统版本”决定了它能跑什么、不能跑什么。NVIDIA 的 GPU 也有一张类似的“系统版本号”,它不叫 Android 13,而叫 Compute Capability(计算能力,简称 CC)。 CC 是 NVIDIA 给每一代 GPU 架构贴上的版本标签:CC 3.0 是 Kepler 架构,CC 8.0 是 Ampere 架构,CC 9.0 是 Hopper 架构。这张“版本号”决定了这个 GPU 支持哪些 CUDA 功能集、最多能同时容纳多少线程、能处理哪些数据类型(比如 FP16、FP8),以及有没有 Tensor Core 这个 AI 加速引擎。对做 AI 的人来说,看懂 CC 就像看懂手机系统版本——你写代码前,先看一眼目标 GPU 的“系统版本”,就知道哪些优化手段能用、哪些要绕道。

岗位关联

计算能力是 AI 芯片入门的“体检报告”:看懂它,你就能在写 CUDA 代码前判断程序能否在目标 GPU 上运行,并为异构设备写出兼容代码。这是 AI 工程化必备的硬件素养。

目录
  1. 可视化演示
  2. 概念本质:GPU 的“系统版本号”
  3. 代际演进:从 Kepler 到 Hopper 的功能跃迁
  4. 核心参数:线程三围与块容量
  5. 数据类型:从 FP64 到 FP8 的精度族谱
  6. 张量核心:从 Volta 到 Hopper 的专用引擎
  7. 特性映射:CC 与 CUDA 版本的契约
  8. 开发实战:一份跨世代的兼容手册
  9. AI 应用场景
  10. Python 演示
  11. 常见误区
  12. 面试题

可视化演示

展示GPU架构版本演进时间线、硬件特性支持矩阵及-gencode编译策略三种核心模式,直观呈现Compute Capability从Kepler到Hopper的演进历程。

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

GPU架构版本演进 从Kepler到Hopper七代GPU架构CC版本演进 Hopper(CC9.0)引入FP8/TMA等突破性特性 K3.x → M5.x → P6.x → V7.0 → T7.5 → A8.x → H9.0 CC版本号直接编码GPU硬件能力与支持的指令集

概念本质:GPU 的“系统版本号”

想象你打开手机设置,滑到“关于手机”,看到一行小字:Android 版本 13。看到这行字,你立刻就知道:这个手机能不能装最新的 App、能不能开息屏显示、能用哪些相机新功能。GPU 的 Compute Capability 就是印在显卡“关于本机”里的那行版本号——只不过它不叫 Android,而叫 CC 8.0 或 CC 9.0。

NVIDIA 给每一代 GPU 架构分配一个形如 X.Y 的编号,X 是主版本号,代表架构的大代际,Y 是次版本号,代表同代内的小幅增强。比如 CC 8.0 是 Ampere 架构,CC 8.6 是 Ampere 的增强版。这个编号不是性能分数,而是一份“功能清单的索引”:查到这个编号,就能查到这张 GPU 支持哪些 CUDA 功能集、允许开多少个线程、能处理什么数据类型。

这份“索引”之所以重要,是因为 GPU 硬件一旦流片就无法改变,但软件可以针对不同版本做适配。CC 就是 NVIDIA 与开发者之间签订的“硬件契约”:开发者按 CC 版本写代码,就能精确控制程序在哪种硬件上运行哪些代码路径。

所以,当你听到“这张卡是 CC 8.6”,不要把它当成“跑分 8.6 分”,而要理解成“这部手机是 Android 13(增强版)”——它宣告了硬件的能力边界,也预告了软件能够施展的空间。

CC=(Major,Minor)\text{CC} = (\text{Major}, \text{Minor})
计算能力就是一对“主版本号 + 次版本号”,主版本定代际,次版本定增强,例如 CC 8.6 表示 Ampere 代际的 6 号增强版。
  1. CC 符号CC 是 Compute Capability 的缩写,代表 GPU 架构版本号,是硬件功能集的“身份证”。
    CC\text{CC}
  2. Major 主版本号主版本代表 GPU 架构的大代际,如 Kepler 是 3,Volta 是 7,Ampere 是 8,每次换代都会引入突破性功能。
    Major∈{3,5,6,7,8,9}\text{Major} \in \{3, 5, 6, 7, 8, 9\}
  3. Minor 次版本号次版本代表同一代内的小步升级,如 Turing 是 7.5,在 Volta 的 7.0 基础上增加了 RT Core 与 INT8 张量加速,但大架构未变。
    Minor∈{0,1,2,3,5,6,7}\text{Minor} \in \{0, 1, 2, 3, 5, 6, 7\}
  4. 组合表示两个数字合起来读作“几点几”,开发者看到 CC $8.6$,就能立刻定位到 Ampere 增强代的全部特性。
    CC=(Major,Minor)⇒CC 8.6\text{CC} = (\text{Major}, \text{Minor}) \Rightarrow \text{CC } 8.6

代际演进:从 Kepler 到 Hopper 的功能跃迁

手机的 Android 版本从 4.0 一路升到 14,每次大版本都带来一批新特性:4.0 有了流畅的界面,5.0 引入 Material Design,10.0 推出深色模式,13 强化隐私保护。GPU 的 CC 版本也走过了同样的进化之路,每一代都解锁一批新“权限”。

2012 年的 Kepler(CC 3.x)首开动态并行,让 GPU 能自己“招兵买马”启动新内核;2014 年的 Maxwell(CC 5.x)与 2016 年的 Pascal(CC 6.x)打磨了统一寻址与 FP16 支持;2017 年的 Volta(CC 7.0)是历史转折点,首次搭载 Tensor Core,为 AI 矩阵运算开辟了专用通道;2018 年的 Turing(CC 7.5)加入 RT Core 并强化张量整数运算;2020 年的 Ampere(CC 8.0)把 Tensor Core 推进到第三代,并首次支持 FP8;2022 年的 Hopper(CC 9.0)则直接为 Transformer 大模型设计。

这种演进有一个重要规律:前向兼容。就像 Android 13 的手机依然能运行为 Android 4 写的 App,高 CC 的 GPU 也能运行为低 CC 写的 CUDA 代码(在软件层面)。这保证了老代码不会因为换卡而作废,也让开发者可以放心追求新特性。

回头再看这张时代年表:从 Kepler 到 Hopper,每一代 CC 都像一次系统大版本更新,把 GPU 从“纯并行计算器”进化成“AI 专用加速器”。开发者需要做的,就是在升级的洪流中,找准自己目标硬件的“系统版本”。

支持(f,CC)  ⟹  支持(f,CC′),∀CC′≥CC\text{支持}(f, CC) \implies \text{支持}(f, CC'), \quad \forall CC' \geq CC
如果一个功能在低版本 CC 上可用,那么在所有更高版本 CC 上也可用——硬件在升级,软件的老承诺不会作废。
  1. 支持函数$\text{支持}(f, CC)$ 是一个判断句,含义是“功能 $f$ 在架构版本 $CC$ 上可用”,比如 $\text{支持}(\text{FP8}, 8.0)$ 为真,而 $\text{支持}(\text{FP8}, 7.0)$ 为假。
    支持(f,CC)\text{支持}(f, CC)
  2. 前向蕴含箭头 $\implies$ 表示“如果……那么……”,即左边成立时右边必然成立,把“低版本支持”这个条件升级为“高版本也支持”的保证。
    支持(f,CC)  ⟹  支持(f,CC′)\text{支持}(f, CC) \implies \text{支持}(f, CC')
  3. 全称量词$\forall$ 读作“对于任意”,它把所有不小于当前 CC 的版本都圈了进来,构成了兼容性的完整承诺。
    ∀CC′≥CC\forall CC' \geq CC

核心参数:线程三围与块容量

把 GPU 想象成一家大型工厂,每个 SM(流式多处理器)是一个车间,线程是车间里的工人。CC 版本决定了一间车间最多能站多少工人、每个班组多少人、工厂最大能接多大的订单。这些“工人容量”是写 CUDA 代码时最容易踩的硬边界。

先看每 SM 线程上限:在 CC 低于 7.0 的老架构上,一个车间最多容纳 1024 名工人;从 Volta(CC 7.0)开始,这个上限翻倍到 2048。再看每块线程上限,也就是一个“班组”的人数,主流架构统一限制为 1024 人,超出就必须拆成多个线程块。网格维度上限是 2^31-1,这是一个几乎不会触顶的天花板,但知道它存在能避免极端 bug。

还有一项容易被忽略的容量是共享内存:它相当于车间里的“公用工具箱”,Kepler 时代每块最多 48KB,到 Hopper 时代已扩展到 228KB。这些参数都不是开发者能改的,而是 CC 版本刻在硬件上的“物理红线”——程序一旦超限,CUDA 运行时会直接拒绝启动内核。

记住一个实用口诀:写内核时,让“每块线程数 × 块数”远小于硬件上限,并用 cudaOccupancyMaxPotentialBlockSize 查询最优配置。CC 就是这张容量表的页码,翻到对应页,你才知道这间车间到底能塞进多少工人。

线程/SM={1024CC<7.02048CC≥7.0\text{线程/SM} = \begin{cases} 1024 & CC < 7.0 \\ 2048 & CC \geq 7.0 \end{cases}
每 SM 的线程容量以 CC 7.0 为分水岭:老架构 1024,新架构 2048。写内核时若超过上限,程序会启动失败。
  1. 条件表达式花括号表示分段函数,根据 CC 与 7.0 的大小关系,选择不同的线程上限值。
    线程/SM={1024CC<7.02048CC≥7.0\text{线程/SM} = \begin{cases} 1024 & CC < 7.0 \\ 2048 & CC \geq 7.0 \end{cases}
  2. 旧架构分支$CC < 7.0$ 覆盖 Kepler 到 Pascal,此时每 SM 最多容纳 1024 个线程,约等于一个“小车间”。
    CC<7.0⇒1024CC < 7.0 \Rightarrow 1024
  3. 新架构分支$CC \geq 7.0$ 覆盖 Volta 及之后的所有架构,上限翻倍到 2048,这也是现代 AI 训练能塞下更大数据块的硬件基础。
    CC≥7.0⇒2048CC \geq 7.0 \Rightarrow 2048

数据类型:从 FP64 到 FP8 的精度族谱

手机拍照时有“画质档位”:RAW 原图最清晰但文件巨大,JPEG 压缩后体积小但细节有损。GPU 处理数字也有类似的“画质档位”,叫做浮点精度。CC 版本决定了这张 GPU 支持哪些精度档位,从最高的 FP64 到最省的 FP8,每一档都是一笔权衡。

浮点数的本质是“用有限的位数表示实数”,格式由三部分组成:1 位符号(正负)、若干位指数(大小量级)、若干位尾数(有效数字)。FP64 用 64 位中的 52 位存尾数,精度高但开销大,主要用于科学计算;FP32 是默认档,AI 框架的默认权重和激活都用它;FP16 把位数砍半,适合深度学习训练,从 Pascal(CC 6.0)开始受到原生支持;FP8 则是为推理和混合精度训练而生的“极致压缩档”,从 Ampere(CC 8.0)开始引入。

FP8 内部还有两种口味:E4M3 用 4 位指数 + 3 位尾数,动态范围小但精度更高,适合权重;E5M2 用 5 位指数 + 2 位尾数,范围更大但精度更低,适合梯度。选档位就像选照片格式:要细节用 FP32,要速度用 FP8,而硬件支不支持某个档位,全看 CC 版本这一张“系统版本号”。

在训练大模型时,选择数据类型就像选照片格式:FP32 是 RAW 原图,FP8 是高度压缩的 JPEG。而 CC 版本,就是相机的“型号列表”——只有 Ampere、Hopper 这两代新相机,才支持 FP8 这个最省空间的格式。

x=(−1)s×2E−bias×(1+M2m)x = (-1)^s \times 2^{E - \text{bias}} \times \left(1 + \frac{M}{2^m}\right)
FP8 的数值公式与 FP32 同源,差别只在位数分配:E4M3 追求精度,E5M2 追求范围。CC 8.0 以下硬件无法硬件加速 FP8。
  1. 符号位 s$s$ 取 0 或 1,$(-1)^s$ 决定正负号,这是所有浮点格式共有的第一位。
    (−1)s(-1)^s
  2. 指数部分$2^{E - \text{bias}}$ 决定数值的量级,$E$ 是存储的指数位编码,$\text{bias}$ 是偏移量,让指数能表达负数(如 E4M3 的 bias 是 7)。
    2E−bias2^{E - \text{bias}}
  3. 尾数部分$1 + \frac{M}{2^m}$ 表示有效数字,其中 $m$ 是尾数位数,E4M3 中 $m=3$,E5M2 中 $m=2$,尾数位越多精度越高。
    1+M2m1 + \frac{M}{2^m}
  4. 参数对照E4M3 的 $bias=7, m=3$,范围小但精度优;E5M2 的 $bias=15, m=2$,范围更大但精度牺牲,两者按角色分工使用。
    E4M3:(bias,m)=(7,3),E5M2:(bias,m)=(15,2)\text{E4M3}: (bias, m) = (7, 3), \quad \text{E5M2}: (bias, m) = (15, 2)

张量核心:从 Volta 到 Hopper 的专用引擎

车间里除了全能工人(CUDA Core),还有一类“专用焊接机器人”——Tensor Core。普通工人什么活都能干,但一次只能算一个数;焊接机器人只会干一种活——矩阵乘加,但一次能算一大片。对 AI 而言,矩阵乘法就是最核心的“焊接活”,所以 Tensor Core 的代际升级几乎决定了 AI 训练的速度天花板。

Volta(CC 7.0)点亮了第一代 Tensor Core,只支持 FP16 矩阵乘,立刻把训练速度提升一个数量级;Turing(CC 7.5)推出第二代,加入 INT8 与 INT4 整型加速,为推理铺路;Ampere(CC 8.0)的第三代 Tensor Core 全面进化,支持 TF32、BF16、FP16 乃至 FP8,一张 A100 的稀疏矩阵算力可超过 300 TFLOPS;Hopper(CC 9.0)更进一步,推出 Transformer Engine,能在训练中自动切换 FP8 与 FP16,为大模型训练而生。

Tensor Core 的能力可以用一个“算力公式”来刻画,它把架构参数转化为每秒能完成的浮点运算次数。从公式中可以看出,决定算力的不是单一频率,而是 SM 数量、核心数量、每周期吞吐的乘积——所有这些参数,都随 CC 代际而变。

所以,当你在选型表上看到“FP8 Tensor Core“这个词,请自动翻译成”这台设备有 Ampere 或更新的架构版本,CC ≥ 8.0“。Tensor Core 的代际,就是 AI 算力的”引擎排量”,而 CC 就是引擎盖上那行铭牌。

TFLOPS=SM数×每SM核数×频率×每周期运算数1012\text{TFLOPS} = \frac{\text{SM数} \times \text{每SM核数} \times \text{频率} \times \text{每周期运算数}}{\text{10}^{12}}
算力是“车间数 × 机器人数量 × 手速 × 每次出手效率”的乘积,分子结构随 CC 代际升级,而 Tensor Core 让“每次出手效率”暴增。
  1. SM 数GPU 里流式多处理器的总数,相当于工厂里的车间数量,Ampere 的 A100 有 108 个 SM。
    SM数\text{SM数}
  2. 每 SM 核数每个车间里 Tensor Core 的数量,Volta 每 SM 有 8 个,Ampere 增加到 4 个更强大的第三代核心(按 SM 内布局不同)。
    每SM核数\text{每SM核数}
  3. 频率与每周期运算数频率是“机器人手速”,每周期运算数是“每次挥手做几次乘加”——Tensor Core 单条指令能完成 $4\times4$ 矩阵乘,这是它碾压普通核心的关键。
    频率×每周期运算数\text{频率} \times \text{每周期运算数}
  4. 单位换算除以 $10^{12}$ 把每秒运算次数换算成 TFLOPS(每秒万亿次浮点运算),这是衡量 AI 加速器的主流指标。
    ⋯1012\frac{\cdots}{\text{10}^{12}}

特性映射:CC 与 CUDA 版本的契约

安卓应用商店里,每个 App 都标着“最低系统要求:Android 10 及以上”。你点安装时,系统会先检查手机版本,不够就直接拒绝。CUDA 开发中同样存在这样的“安装检查机制”——功能与 CC 版本之间的契约,由两套机制来执行。

第一套是编译期检查,靠 __CUDA_ARCH__ 宏实现。它是一个预定义宏,在编译不同架构的内核时会自动变成对应的 CC 数字,比如编译 compute_80 时它等于 800。开发者可以用条件编译写出“多版本兼容”的代码路径:CC ≥ 8.0 时启用 FP8 路径,否则走通用回退。第二套是运行时查询,调用 cudaGetDeviceProperties 拿到 major/minor 字段,程序跑起来后再决定调用哪个内核。

两套机制合起来形成了“支持矩阵”:横轴是 CUDA 版本,纵轴是 CC 版本,表格里标着每个版本组合支持哪些功能。比如 CUDA 11.0 首次完整支持 Ampere 的 CC 8.0,CUDA 12.0 开始支持 Hopper 的 CC 9.0。这套矩阵就是开发者查“我的软件配什么硬件”的官方字典。

如果用 C 语言写条件编译,逻辑是这样的:

这就像 App 开发者判断“设备是否支持灵动岛”:在编译期就把代码分成两套,运行时由系统自动选用。CC 版本就是这条"if-else"的分水岭。

supported(f,d)={1CCd≥CCmin⁡(f)0otherwise\text{supported}(f, d) = \begin{cases} 1 & \text{CC}_d \ge \text{CC}_{\min}(f) \\ 0 & \text{otherwise} \end{cases}
一个功能能否在设备上使用,等价于“设备 CC 大于等于该功能的最低 CC 要求”。这是所有 CUDA 兼容性代码的底层逻辑。
  1. 判定函数$\text{supported}(f, d)$ 返回 1 表示功能 $f$ 在设备 $d$ 上可用,返回 0 表示不可用。
    supported(f,d)\text{supported}(f, d)
  2. 设备 CC$\text{CC}_d$ 是设备 $d$ 的计算能力,运行时从 cudaDeviceProp 结构体中读取的 major 与 minor 字段。
    CCd\text{CC}_d
  3. 最低要求$\text{CC}_{\min}(f)$ 是功能 $f$ 的最低 CC 门槛,比如 FP8 的 $\text{CC}_{\min} = 8.0$,低于它硬件就没有对应电路。
    CCmin⁡(f)\text{CC}_{\min}(f)
  4. 大小比较当设备 CC 大于等于门槛时输出 1,否则输出 0——这就是“系统版本不够,App 不让装”的数学化表达。
    CCd≥CCmin⁡(f)\text{CC}_d \ge \text{CC}_{\min}(f)
cuda5 行
#if __CUDA_ARCH__ >= 800    // 使用 FP8 Tensor Core 的代码路径#else    // 通用 FP16/FP32 回退路径#endif

开发实战:一份跨世代的兼容手册

做过安卓开发的都知道,想让 App 同时跑在 Android 8 和 Android 14 上,不能只写一套代码。GPU 开发同理:当你面对一个混用 A100(CC 8.0)和 H100(CC 9.0)的集群时,Compute Capability 就是你规划兼容策略的“台历”。

第一步是编译期做多架构编译。用 -gencode arch=compute_80,code=sm_80 和 -gencode arch=compute_90,code=sm_90 让编译器同时生成两份 SASS 机器码,嵌入同一个 fatbin,运行时由驱动按实际硬件自动挑选。第二步是运行时做分支适配:程序启动时查询 cudaDeviceProp.major/minor,根据 CC 决定加载哪个内核、使用哪条优化路径。第三步是硬件选型:在训练大模型前,查支持矩阵确认目标 GPU 的 CC ≥ 8.0,否则 FP8 混合精度训练无法硬件加速。

整体兼容性可以用一个逻辑表达式来概括:程序能在 GPU 上跑通,等于它用到的每一个功能都满足各自的 CC 门槛。任何一项不满足,整个程序就要么降级、要么报错。这个“与”的逻辑,就是开发者心中那张兼容性清单的原型。

实践里,很多团队会维护一张“CC 兼容表”:列出公司所有 GPU 的 CC 版本、支持的特性和推荐的编译选项。这张表就像手机的“可用机型清单”,让开发者在写代码前就避开雷区,而不是等部署时才发现“这卡不支持 FP8”。CC 不是一道考试分数,而是一份需要随身携带的硬件地图。

Compat(代码,GPU)=⋀f∈代码[CCGPU≥CCmin⁡(f)]\text{Compat}(代码, GPU) = \bigwedge_{f \in 代码} \left[ \text{CC}_{GPU} \ge \text{CC}_{\min}(f) \right]
程序与 GPU 兼容,等价于“代码用到的每个功能都通过了 CC 门槛检查”。编译器把这份清单逐项打钩,全部通过才放行。
  1. 兼容谓词$\text{Compat}(代码, GPU)$ 是一个布尔值,表示整份代码能否在目标 GPU 上正确运行。
    Compat(代码,GPU)\text{Compat}(代码, GPU)
  2. 逻辑与连乘$\bigwedge$ 是“对所有项做逻辑与”,相当于逐个打勾,任何一个功能不满足条件,整体结果就是假。
    ⋀f∈代码\bigwedge_{f \in 代码}
  3. 逐功能检查对代码中用到的每个功能 $f$,判断“设备 CC 是否大于等于该功能的最低 CC 要求”,并把所有判断结果汇总。
    [CCGPU≥CCmin⁡(f)]\left[ \text{CC}_{GPU} \ge \text{CC}_{\min}(f) \right]

AI 应用场景

大模型训练前的硬件选型

阶段:硬件规划与资源评估

解决的问题:过去团队选 GPU 只看显存大小,结果买了显存很大但 CC 只有 6.0 的旧卡,跑 FP16 混合精度训练时没有 Tensor Core 加速,训练速度慢一个数量级。通过核对 CC 版本,可以在采购前确认目标 GPU 是否支持 FP8、有无第三代 Tensor Core。

具体案例:某团队准备训练 70B 参数大模型,预计单卡需要 80GB 显存。他们用 CC 支持矩阵筛选,发现 A100(CC 8.0)支持 FP8 但 FP8 算力为 0(标注为训练优化),H100(CC 9.0)才完整支持 FP8 加速,最终选择 H100 集群,训练吞吐提升了约 30%。

推理服务部署的算子兼容适配

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

解决的问题:生产环境常常混用多代 GPU(如 V100 与 A100 并存),若代码只按单一代际优化,在老卡上会崩溃或性能骤降。利用 CC 分支适配,运行时选择对应的算子实现。

具体案例:一家 AI 公司把同一套推理服务部署到 V100(CC 7.0)和 A100(CC 8.0)混部集群。代码中用 __CUDA_ARCH__ >= 800 编译出 FP8 快速路径与 FP16 回退路径,运行时通过 cudaGetDeviceProperties 选择内核,服务在两类卡上都稳定运行。

异构集群的容器镜像多架构配置

阶段:环境构建与发布

解决的问题:一次构建的容器镜像要跑在 CC 7.0 到 CC 9.0 的多种 GPU 上,如果只编译单一架构,镜像会在低版本 GPU 上无法启动。通过多架构编译生成 fatbin,一份镜像通用全集群。

具体案例:某云厂商在镜像构建阶段执行 -gencode arch=compute_70,code=sm_70 与 arch=compute_80,code=sm_80 两套编译,覆盖从 V100 到 A100 的用户实例,镜像体积仅增加 15%,但兼容性覆盖了 95% 的存量 GPU。

Python 演示

python57 行
"""查询 GPU 计算能力并判断支持的特性"""import osfrom collections import namedtuple Device = namedtuple("Device", ["name", "cc_value", "memory_gb"]) ARCH_TABLE = {    3.0: ("Kepler", ["Dynamic Parallelism"]),    5.0: ("Maxwell", ["Cooperative Groups"]),    6.0: ("Pascal", ["FP16", "Unified Memory"]),    7.0: ("Volta", ["FP16 Tensor Core"]),    7.5: ("Turing", ["INT8 Tensor Core"]),    8.0: ("Ampere", ["FP8", "Gen3 Tensor Core", "TF32"]),    9.0: ("Hopper", ["Transformer Engine", "FP8"]),} def query_device(device_id=0):    """从真实环境查询 GPU;无 CUDA 时返回模拟的 A100 数据"""    try:        import torch        name = torch.cuda.get_device_name(device_id)        major, minor = torch.cuda.get_device_capability(device_id)        cc_value = major + minor / 10.0        mem_gb = torch.cuda.get_device_properties(device_id).total_memory // (1024 ** 3)        return Device(name=name, cc_value=cc_value, memory_gb=mem_gb)    except (ImportError, AssertionError, RuntimeError):        return Device(name="NVIDIA A100-SXM4-40GB", cc_value=8.0, memory_gb=40) def get_arch(cc_value):    """根据 CC 查架构代号与特性列表,返回不高于该 CC 的最新世代"""    best = None    for arch_cc, (name, feats) in sorted(ARCH_TABLE.items()):        if cc_value >= arch_cc:            best = (arch_cc, name, feats)    return best or (0.0, "Unknown", []) def check_feature(dev, feature):    """判断某特性是否被该设备支持"""    _, _, feats = get_arch(dev.cc_value)    return feature in feats def main():    dev = query_device()    arch_cc, arch_name, feats = get_arch(dev.cc_value)    print(f"GPU: {dev.name}")    print(f"显存: {dev.memory_gb} GB")    print(f"Compute Capability: {dev.cc_value:.1f}")    print(f"架构代号: {arch_name}")    print(f"核心特性: {', '.join(feats)}")    print(f"支持 FP8: {check_feature(dev, 'FP8')}")    print(f"支持 Gen3 Tensor Core: {check_feature(dev, 'Gen3 Tensor Core')}")    skip = os.environ.get("SKIP_CUDA_CHECK")    if skip:        print("跳过 CUDA 运行时检查") if __name__ == "__main__":    main()
目的:一键查询本机 GPU 的计算能力,自动映射到架构代号,并判断 FP8、第三代 Tensor Core 等关键特性是否可用,用于硬件选型与兼容性检查。
  1. Device = namedtuple(...)定义一个轻量数据结构,把 GPU 名称、CC 值、显存打包在一起,方便后续读写。
  2. ARCH_TABLE = {...}用字典建立“CC 版本 → 架构代号 + 特性列表”的支持矩阵,这是整个判断逻辑的知识库。
  3. torch.cuda.get_device_capability(device_id)从 PyTorch 拿到真实 GPU 的主次版本号,是查询 CC 的最简接口。
  4. cc_value = major + minor / 10.0把 (8, 0) 这样的二元组转成 8.0 浮点数,便于用数值比较大小。
  5. except (ImportError, AssertionError, RuntimeError)无 GPU 环境下返回模拟的 A100 数据,保证代码在任何机器上都能演示。
  6. for arch_cc, (name, feats) in sorted(ARCH_TABLE.items())按 CC 从小到大遍历,找到“不高于目标 CC 的最新世代”,这是兼容性判断的核心逻辑。
  7. check_feature(dev, 'FP8')把“设备是否支持某特性”抽象成函数调用,底层就是比对特性列表,对应文中“支持矩阵”的查询过程。

常见误区

误区“CC 8.0 比 CC 7.0 高,所以 CC 8.0 的 GPU 一定比 CC 7.0 快一倍。”很多初学者把 CC 当成绩单,认为版本号越大性能越强。

正解CC 是“功能集版本号”,不是性能分数。就像 Android 14 的手机不一定比 Android 10 的手机跑得快——性能还取决于处理器、内存、散热。CC 只告诉你“支持哪些功能”,例如 A100(CC 8.0)的 FP32 算力其实低于 V100(CC 7.0)同功耗版本,但它多了 FP8 与第三代 Tensor Core。

为何常见:版本号天然带有“越大越新、越强”的心理暗示,加上厂商宣传时总强调最新代际,容易让人把“代数”误读为“速度”。

误区“CUDA 11.8 和 CC 11.8 是一回事吧?我的 GPU 支持 CUDA 11.8。”初学者查资料时看到两个 11.8 就搞混了。

正解CC 是硬件架构版本(刻在 GPU 上,无法升级),CUDA 是软件工具包版本(可以安装新版本)。用手机类比:CC 是手机的系统版本,CUDA 是 App 商店版本——手机系统是 Android 10,App 商店可以更新到最新版,但商店里“要求 Android 12”的 App 照样装不上。

为何常见:两者都是数字编号,且 CUDA 文档里经常并列出现(如"CUDA 11.8 supports CC 3.5-8.6"),读快了就容易张冠李戴。

误区“既然 FP8 能省一半显存,那我的 V100 应该也能用 FP8 训练吧?”有些同学以为 FP8 只是软件层面的格式,任何 GPU 都能算。

正解FP8 需要硬件原生支持,包括专用的 FP8 Tensor Core 流水线。V100(CC 7.0)的 Tensor Core 只支持 FP16,Turing(CC 7.5)支持 INT8,只有 Ampere(CC 8.0)及之后才在硬件层面支持 FP8。在旧卡上跑 FP8 代码,要么被编译器拒绝,要么被降级为 FP16 模拟,不仅没提速反而更慢。

为何常见:FP8 听起来像“降低精度”,似乎只是软件层面的取舍,容易忽略它是需要专用电路才能加速的硬件特性。

误区“CC 7.5 的 Tesla T4 和 RTX 2060 既然计算能力一样,性能也应该一样。”少数派同学只看版本号忽略了具体的 SM 数量与显存配置。

正解CC 只是“功能集契约”,相同的 CC 保证软件层面看到的功能一致,但性能参数(SM 数、频率、显存带宽、Tensor Core 数量)由具体型号决定。比如同为 CC 8.0,A100 与 RTX 3080 的算力天差地别,但都能运行同版本 CUDA 代码。

为何常见:人脑喜欢把“相同版本号”简化成“相同产品”,忽略了 CC 只是众多硬件属性中的一个维度。

误区“CC 7.0 和 CC 7.5 差不多,都是 Volta 那一代的,代码通用就行。”有的同学只记主版本,觉得小数位无所谓。

正解次版本号代表同代内的显著增强,例如 Turing 的 CC 7.5 比 Volta 的 CC 7.0 多了 RT Core 与 INT8 Tensor Core,FP16 吞吐也翻倍。代码里用 __CUDA_ARCH__ >= 750 判断的特性,在 CC 7.0 上就是不可用的。忽略次要版本,会在部署时遇到“明明说支持,实际却没有”的怪问题。

为何常见:初学时先记大版本(7、8、9),容易把次版本当成“补丁号”而不予重视。

面试题

Q1. Compute Capability 6.1 中的数字 "6" 代表什么?

单选题 · easy

  • A.主版本号,代表架构代际← 正确答案
  • B.次版本号,同代小幅升级
  • C.显存容量,单位为 GB
  • D.CUDA 软件包的版本号

答案:A。CC = (Major, Minor),前一位是主版本号,标志架构代际(如 6 表示 Pascal);后一位是次版本号。显存容量与 CUDA 版本都与 CC 无关,所以 B、C、D 均错。

Q2. 以下哪个架构对应的计算能力是 CC 8.0?

单选题 · easy

  • A.Volta 架构
  • B.Turing 架构
  • C.Ampere 架构← 正确答案
  • D.Hopper 架构

答案:C。Volta 是 CC 7.0,Turing 是 CC 7.5,Ampere 是 CC 8.0,Hopper 是 CC 9.0。记住“8 开始有 FP8”,就能把 Ampere 与 8.0 绑在一起。

Q3. 现代 NVIDIA GPU(CC ≥ 7.0)中,每个 SM 最多支持的线程数是多少?

单选题 · mid

  • A.512 个线程
  • B.1024 个线程
  • C.2048 个线程← 正确答案
  • D.4096 个线程

答案:C。CC 7.0 之前每 SM 上限为 1024,Volta 起翻倍到 2048。这个上限决定了内核的占用率规划——超过上限则内核启动失败。512/4096 都不是硬件规定的标准上限。

Q4. FP8 格式中的 "E4M3" 表示什么?

单选题 · mid

  • A.4 位符号、3 位指数的布局
  • B.4 位指数、3 位尾数的布局← 正确答案
  • C.4 位尾数、3 位指数的布局
  • D.4 位指数、3 位符号的布局

答案:B。E4M3 表示 1 位符号 + 4 位指数 + 3 位尾数,E5M2 表示 1 位符号 + 5 位指数 + 2 位尾数。E 指 Exponent(指数),M 指 Mantissa(尾数),所以 A、C、D 都错。

Q5. 哪一代 GPU 架构首次引入 Tensor Core?

单选题 · mid

  • A.Pascal 架构
  • B.Volta 架构← 正确答案
  • C.Turing 架构
  • D.Ampere 架构

答案:B。Volta(CC 7.0)在 2017 年首次搭载第一代 Tensor Core,专为 FP16 矩阵乘设计;Turing 是第二代,Ampere 是第三代,Pascal 则完全没有 Tensor Core。

Q6. __CUDA_ARCH__ 宏的主要用途是什么?

单选题 · hard

  • A.运行时查询 GPU 的核心频率
  • B.编译期判断目标 GPU 架构版本← 正确答案
  • C.计算 GPU 的显存带宽数值
  • D.管理线程块之间的同步操作

答案:B。__CUDA_ARCH__ 是 CUDA 编译期的预定义宏,编译 compute_80 时其值为 800,开发者据此用条件编译生成多架构代码。频率查询与线程同步分别由 cudaDeviceProp 与 __syncthreads() 完成。

Q7. 一张 CC 7.5 的 Turing GPU 无法执行下列哪类代码?

单选题 · hard

  • A.使用动态并行特性的代码
  • B.使用统一内存寻址的代码
  • C.使用 FP8 Tensor Core 的代码← 正确答案
  • D.使用 FP16 半精度计算的代码

答案:C。FP8 从 Ampere(CC 8.0)才开始硬件支持,Turing 的 Tensor Core 只支持 FP16 与 INT8。动态并行自 Kepler 起就有,统一内存与 FP16 在 Pascal 时代就已支持,所以 A、B、D 都能执行。

Q8. 以下哪些能力由 Compute Capability 决定?

多选题 · easy

  • A.每 SM 的最大线程数← 正确答案
  • B.支持的数据类型范围← 正确答案
  • C.显卡的物理尺寸大小
  • D.Tensor Core 的代数← 正确答案

答案:ABD。CC 是功能集版本号,决定线程上限、数据类型与 Tensor Core 代际。物理尺寸由散热与 PCB 设计决定,跟 CC 完全无关,所以 C 不选。

Q9. Ampere(CC 8.0)相比上一代引入了哪些新特性?

多选题 · mid

  • A.原生 FP8 支持← 正确答案
  • B.第三代 Tensor Core← 正确答案
  • C.光线追踪 RT Core
  • D.TF32 混合精度格式← 正确答案

答案:ABD。Ampere 首次支持 FP8、升级到第三代 Tensor Core,并新增 TF32。RT Core 其实是 Turing(CC 7.5)引入的,所以 C 不选。

Q10. 关于 Compute Capability 的兼容性,下列说法正确的有?

多选题 · hard

  • A.CC 较高的 GPU 能运行旧特性代码← 正确答案
  • B.CC 相同的 GPU 性能一定相同
  • C.编译期可用 CUDA_ARCH 分支← 正确答案
  • D.CC 就是 CUDA 软件版本号

答案:AC。CC 具有前向兼容性,高版本可运行低版本特性,A 正确;__CUDA_ARCH__ 用于编译期分支,C 正确。CC 相同的 GPU 功能集相同但性能取决于 SM 数、频率等,B 错;CC 是硬件版本而 CUDA 是软件版本,D 错。