什么是GPU

现代机器学习GPU(例如H100、B200)基本上是由多个专门用于矩阵乘法的计算核心(称为流式多处理器或SM)连接到一块高速内存条(称为HBM)组成。下图所示:

图: H100 或 B200 GPU 的抽象布局示意图。H100 有 132 个 SM(流式多处理器),而 B200 有 148 个。我们用"Warp Scheduler"(线程束调度器)一词来泛指一组 32 个 CUDA SIMD 核心以及将工作分配给它们的调度器。请注意,它看起来很像 TPU!

每个SM(就像TPU的张量核心一样)都有一个专用的矩阵乘法核心(不幸的是,它也被称为张量核心)。),一个向量算术单元(称为Warp 调度器) 。)以及高速片上缓存(称为SMEM)。与最多只有2个独立"张量核心"的TPU不同,现代GPU拥有超过100个SM(H100上为132个)。虽然每个SM的性能远低于TPU的张量核心,但整个系统更加灵活。每个SM几乎完全独立,因此GPU可以同时执行数百个不同的任务。

让我们更详细地了解一下H100 SM:

图: H100 SM 的示意图(来源),图中显示了 4个子分区,每个子分区包含一个张量核心、一个线程束调度器、一个寄存器文件以及不同精度的 CUDA 核心组。底部附近的"L1 数据缓存"是 256kB 的 SMEM 单元。B200 的结构与之类似,但增加了大量的张量内存 (TMEM),用于为体积庞大的张量核心提供数据。

每个SM被划分为4个相同的象限,NVIDIA称之为SM子分区,每个子分区包含一个Tensor Core、16k个32位寄存器以及一个名为Warp Scheduler的SIMD/SIMT向量运算单元,其通道(ALU)NVIDIA称之为CUDA Core。每个分区的核心组件可以说是Tensor Core,它执行矩阵乘法运算,贡献了其绝大部分的FLOPs/s(浮点运算/秒),但它并非唯一值得注意的组件。

  • CUDA 核心:每个子分区包含一组称为 CUDA 核心的 ALU,用于执行 SIMD/SIMT 向量运算。每个 ALU 通常每个周期可以执行 1 个算术运算,例如 f32.add。每个子分区包含 32 个 fp32 核心(以及少量 int32 和 fp64 核心),每个核心在每个周期执行相同的指令。与 TPU 的 VPU 类似,CUDA 核心负责 ReLU 激活函数、逐点向量运算和归约(求和)操作。

  • 张量核心 (TC):每个子分区都有自己的张量核心,它是一个专用的矩阵乘法单元,类似于 TPU MXU。张量核心贡献了 GPU 的绝大多数 FLOPs/s(例如,在 H100 上,我们有 990 bf16 TC TFLOP/s,而 CUDA 核心只有 66 TFLOPs/s)。

    • 990 bf16 TFLOPs/s, 132 个 SM 以 1.76GHz 的频率运行,意味着每个 H100 TC 可以执行7.5e12 / 1.76e9 / 4 ~ 1024bf16 FLOPs/周期,大约相当于 8x8x8 矩阵乘法。

    • 与 TPU 类似,GPU 可以以更高的吞吐量执行低精度矩阵乘法运算(例如,H100 的 fp8 FLOPs/s 是 fp16 的两倍)。低精度训练或服务速度可以显著提升。

    • 自 Volta 架构以来,每一代 GPU 的 TC(时间转换器)容量都比上一代有所增加(相关内容可参阅相关文章)。到了 B200 架构,TC 的容量已经大到无法再将其输入存储在 SMEM(单帧内存)中,因此 B200 引入了一种名为 TMEM(时间存储器)的新内存空间。

CUDA 核心比 TPU 的 VPU 更灵活: GPU CUDA 核心(自 V100 版本起)采用 SIMT(单指令多线程 )编程模型,而 TPU 则采用 SIMD(单指令多数据 )模型。与 TPU VPU 中的 ALU 类似,子分区内的 CUDA 核心必须在每个周期执行相同的操作(例如,如果一个核心正在计算两个浮点数的和,那么子分区中的其他所有 CUDA 核心也必须执行相同的操作)。然而,与 VPU 不同的是,每个 CUDA 核心(或 CUDA 编程模型中的"线程")都有自己的指令指针,可以独立编程 。当同一个 warp 中的两个线程被指示执行不同的操作时,实际上会执行这两个操作,从而屏蔽掉不需要执行不同操作的核心。

**图:**一组线程中线程束发散的示例(来源)。白色区域表示至少一部分物理 CUDA 核心发生停滞。

这使得线程级别的编程更加灵活,但代价是如果线程束发散过于频繁,性能会悄然下降。线程在访问内存方面也更加灵活;VPU 只能操作连续的内存块,而 CUDA 核心可以访问共享寄存器中的单个浮点数,并维护每个线程的状态。

CUDA 核心调度也更加灵活: SM 的运行方式有点像多线程 CPU,因为它们可以同时"调度"许多程序(线程束)(每个 SM 最多 64 个),但每个线程束调度器在每个时钟周期中只执行一个程序。Warp调度器会自动在活动线程束之间切换,以隐藏内存加载等I/O操作。相比之下,TPU通常是单线程的。

记忆

除了计算单元之外,GPU 还具有内存层次结构,其中最大的是 HBM(GPU 主内存),然后是一系列较小的缓存(L2、L1/SMEM、TMEM、寄存器内存)。

  • 寄存器:每个子分区都有自己的寄存器文件,其中包含 H100/B200(每个 SM)上的 16,384 个 32 位字,4 * 16384 * 4 = 256kiBCUDA 内核可以访问该文件。

    • 每个 CUDA 核心一次最多只能访问 256 个寄存器,因此尽管我们可以为每个 SM 调度多达 64 个"驻留线程束",但256 * 1024 / (4 * 32 * 256)如果每个线程使用 256 个寄存器,则一次只能容纳 8 个线程束。
  • SMEM(L1缓存):每个SM都有其自身的256kB片上缓存,称为SMEM。SMEM既可以由程序员控制作为"共享内存",也可以由硬件用作片上缓存。SMEM用于存储TC矩阵乘法器的激活值和输入值。

  • L2缓存:所有SM共享9使用相对较大的约 50MB L2 缓存来减少主内存访问。

    • 它的大小与TPU的VMEM类似,但速度慢得多,而且不受程序员控制。这就导致了一种"远程控制"的现象:程序员需要修改内存访问模式,以确保L2缓存得到充分利用。10

    • NVIDIA并未公布其芯片的L2带宽,但经测量约为5.5TB/s。这大约是HBM带宽的1.6倍,但由于它是全双工的,因此实际双向带宽接近3倍。相比之下,TPU的VMEM容量是其两倍*,*带宽也更高(约为40TB/s)。

  • HBM: GPU 主内存,用于存储模型权重、梯度、激活值等。

    • HBM 容量从 Volta 的 32GB 大幅增加到 Blackwell (B200) 的 192GB。

    • 从 HBM 到 CUDA Tensor Core 的带宽称为 HBM 带宽或内存带宽,在 H100 上约为 3.35TB/s,在 B200 上约为 9TB/s。

GPU规格概要

以下是近期几款GPU的规格概要。同一GPU的不同版本在SM单元数量、时钟频率和浮点运算能力方面略有差异。以下是显存容量数据:

所有世代的SM单元都配备256kB的寄存器内存。Blackwell架构的SM单元还额外配备了256kB的TMEM内存。以下是各芯片的浮点运算性能和带宽数据:

由于 B100 不是批量生产的,所以我们将其排除在外。11某些规格会略微取决于 GPU 的具体版本,因为 NVIDIA GPU 不像 TPU 那样标准化。

以下是一份便于理解的GPU和TPU组件对比表:

芯片级GPU与TPU的比较

GPU 最初是用来渲染视频游戏的,但自从 2010 年代深度学习兴起以来,它们的作用越来越像专用矩阵乘法机------换句话说,越来越像 TPU。12从某种程度上说,这段历史解释了现代GPU的外观。它们的设计初衷并非纯粹为了LLM或机器学习模型,而是作为通用加速器,其硬件追求的"通用性"既是优势也是劣势。GPU在应用于新任务时通常"开箱即用",对优秀编译器的依赖程度远低于TPU。但这同时也使得GPU的性能分析和极限性能挖掘变得更加困难,因为许多编译器特性都可能造成瓶颈。

GPU 的模块化程度更高。TPU拥有 1-2 个大型 Tensor Core,而 GPU 则拥有数百个小型 SM。同样,每个 Tensor Core 包含 4 个大型 VPU,每个 VPU 拥有 1024 个 ALU,而 GPU 的 H100 芯片则拥有 132 * 4 = 528 个独立的 SIMD 单元。以下是 GPU 与 TPU 的 1:1 对比,突显了这一点:

一方面,模块化程度的差异使得TPU的构建成本更低、更易于理解,但另一方面,这也给编译器带来了更大的负担,需要编译器做出正确的处理。由于TPU只有一个控制线程,并且只支持向量化的VPU级指令,编译器需要手动流水线化所有内存加载和MXU/VPU操作,以避免停顿。而GPU程序员可以启动数十个不同的内核,每个内核运行在一个完全独立的SM上。另一方面,这些内核可能会因为频繁访问L2缓存或内存加载无法合并而导致性能极差;由于硬件控制了运行时的大部分过程,因此很难了解其背后的运行机制。因此,TPU通常能够以更少的工作量接近其性能峰值。

从历史数据来看,单个GPU的性能(以及价格)都比同等配置的TPU更强大:单个H200的浮点运算速度(FLOPs/s)几乎是TPU v5p的两倍,HBM显存则是其1.5倍。同时,在谷歌云平台上,H200的标价约为每小时10美元,而TPU v5p的标价仅为每小时4美元。与GPU相比,TPU通常更依赖于多个芯片的联网。

TPU 拥有更大的高速缓存。此外,TPU 的 VMEM 容量也远大于 GPU 的 SMEM(加上 TMEM),这些内存可用于存储权重和激活值,从而实现极快的加载和使用。如果能够持续地将模型权重存储或预取到 VMEM 中,则可以显著提升 LLM 推理的速度。

测验 1:GPU 硬件

这里有一些练习题,可以帮助你检验对以上内容的掌握程度。答案已提供,但最好在查看答案之前,先尝试用笔和纸自己作答。

问题 1 [CUDA 核心]

H100 有多少个 fp32 CUDA 核心(ALU)?B200 又有多少个?这与 TPU v5p 中独立 ALU 的数量相比如何?

: H100 有 132 个 SM,每个 SM 分为 4 个子分区,每个子分区包含 32 个 fp32 CUDA 核心,因此我们有132 * 4 * 32 = 16896CUDA 核心。B200 有148SM,因此总共有CUDA 核心18944。TPU v5p 有 2 个 TensorCore(通常通过 Megacore 连接),每个 TensorCore 包含一个 VPU,该 VPU 具有 (8, 128) 条通道,每条通道有 4 个独立的 ALU,因此2 * 4 * 8 * 128 = 8192有 ALU。这大约是 H100 向量通道数的一半,运行频率大致相同。

问题 2 [向量浮点运算次数计算]

单个 H100 有 132 个 SM 单元,运行频率为 1.59GHz(最高可达 1.98GHz)。假设每个 ALU 每个时钟周期可以执行一次向量运算。每秒可以执行多少次向量 fp32 浮点运算?使用睿频加速后呢?这与矩阵乘法浮点运算次数相比如何?

:使用 Boost 后,其浮点运算速度为 33.5 TFLOPs/s。这只有 规格表132 * 4 * 32 * 1.59e9 = 26.9TFLOPs/s上数据的一半,因为理论上我们可以在一个周期内执行一次 FMA(融合乘加)运算,这算作两次浮点运算,但在大多数情况下这没什么用。我们可以执行 990 TFLOPs/s 的 bfloat16 矩阵乘法运算,因此忽略 FMA 运算,Tensor Core 的浮点运算速度大约是其 30 倍。

问题 3 [GPU 矩阵乘法强度]

H100、B200 和 fp8 的峰值 fp16 矩阵乘法强度分别是多少?强度指的是矩阵乘法浮点运算速度(FLOPs/s)与内存带宽的比值。

:对于 H100,峰值浮点运算能力为 990e12 fp16 FLOPs,带宽为 3.35e12 字节/秒。因此990e12 / 3.35e12 = 295,其临界强度与 TPU 的 240 非常接近。对于 B200 2250e12 / 8e12 = 281,其临界强度也非常接近。这意味着,与 TPU 类似,在矩阵乘法运算中,我们需要大约 280 的批处理大小才能达到计算瓶颈。

对于 H100 和 B200,我们恰好有 2 倍的 fp8 FLOPs,因此峰值强度也分别翻倍至 590 和 562,尽管从某种意义上说,如果我们考虑到权重也可能以 fp8 格式加载,它就保持不变。

问题 4 [MATMU 运行时间]

根据问题 3 的答案,您预计fp16[64, 4096] * fp16[4096, 8192]在单个 B200 上执行 MATMU 操作需要多长时间?如果是呢fp16[512, 4096] * fp16[4096, 8192]

:从以上内容可知,当批处理大小小于 281 个令牌时,通信将受到限制。因此,第一个问题纯粹受带宽限制。我们进行读取或写入操作。

2 B D + 2 D F + 2 B F 字节(2*64*4096 + 2*4096*8192 + 2*64*8192=69e68e12的带宽为字节/秒,因此大约需要69e6 / 8e12 = 8.6us。实际上,我们可能只能利用总带宽的一小部分,因此可能需要接近 10-12 微秒。当我们增加批处理大小时,计算量将完全受限,因此我们预计T=2*512*4096*8192/2.3e15=15us。同样,我们预计只能利用总 FLOPs 的一小部分,因此我们可能会看到接近 20 微秒。

问题 5 [L1 缓存容量]

H100 的总 L1/SMEM 容量是多少?寄存器内存容量是多少?这与 TPU VMEM 容量相比如何?

:每个SM单元有256kB的SMEM和256kB的寄存器内存,各约33MB 132 * 256kB。加起来总共约66MB。这大约是现代TPU的VMEM(120MB)的一半,尽管TPU的总寄存器内存只有256kB!TPU的VMEM延迟低于SMEM延迟,这也是TPU上的寄存器内存并非至关重要的原因之一(VMEM的溢出和填充开销很小)。

问题 6 [计算 B200 时钟频率]

NVIDIA 报告,B200 可以执行 80TFLOPS/s 的向量 fp32 计算。已知每个 CUDA 核心在 FMA(融合乘加)操作中每个周期可以执行 2 FLOPs,请估算峰值时钟周期。

:我们知道有 148 * 4 * 32 = 18944 个 CUDA 核心,所以我们可以做到18944 * 2 = 37888 FLOPs / cycle。因此80e12 / 37888 = 2.1GHz,可以实现一个较高但合理的峰值时钟频率。B200 系列处理器通常采用液冷散热,因此更高的时钟频率更为合理。

问题 7 [估算 H100 加法运算运行时间]

fp32[N]使用上图,计算在单个 H100 上将两个向量相加所需的时间。计算两个向量的加法运算时间。T数学和T通信这个操作的算术强度是多少?如果可以访问,请尝试在 PyTorch 或 JAX 中分别运行此操作,并N = 1024比较N=1024 * 1024 * 1024结果。

:首先,将两个fp32[N]向量相加需要执行 N 次 FLOP 运算,需要4 * N * 2加载字节数,并写回 4 * N 个字节,总共需要3 * 4 * N = 12N。计算它们的比率,我们得到total FLOPs / total bytes = N / 12N = 1 / 12,这非常糟糕。

正如我们上面计算的,忽略 FMA,我们可以获得大约 33.5 TFLOPs/s 的性能提升。这仅在所有 CUDA 核心都被使用的情况下才成立。对于,我们最多 N = 1024只能使用1024 个 CUDA 核心或 8 个 SM,这将耗时更长(假设我们受限于计算能力,则大约需要 16 倍的时间)。此外,我们的内存带宽为 3.35e12 字节/秒。因此,我们的峰值硬件强度为。33.5e12 / 3.35e12 = 1013所以我们将严重受限于通信。因此,我们的运行时环境将非常有限。

对于N = 65,536,这大约是 0.23 微秒。实际上,我们在 JAX 中看到的运行时间约为 1.5 微秒,这没问题,因为我们预计这里会受到极高的延迟限制。对于N = 1024 * 1024 * 1024,我们设定的最高延迟约为 3.84 毫秒,而实际运行时间为 4.1 毫秒,这很好!

相关推荐
AngelPP20 小时前
OpenClaw 架构深度解析:如何把 AI 助手搬到你的个人设备上
人工智能
宅小年20 小时前
Claude Code 换成了Kimi K2.5后,我再也回不去了
人工智能·ai编程·claude
九狼20 小时前
Flutter URL Scheme 跨平台跳转
人工智能·flutter·github
ZFSS21 小时前
Kimi Chat Completion API 申请及使用
前端·人工智能
天翼云开发者社区1 天前
春节复工福利就位!天翼云息壤2500万Tokens免费送,全品类大模型一键畅玩!
人工智能·算力服务·息壤
知识浅谈1 天前
教你如何用 Gemini 将课本图片一键转为精美 PPT
人工智能
Ray Liang1 天前
被低估的量化版模型,小身材也能干大事
人工智能·ai·ai助手·mindx
shengjk11 天前
NanoClaw 深度剖析:一个"AI 原生"架构的个人助手是如何运转的?
人工智能
西门老铁1 天前
🦞OpenClaw 让 MacMini 脱销了,而我拿出了6年陈的安卓机
人工智能