【GPU】GPU架构入门指南 | GPU内部工作原理 | 执行过程 | 如何运行kernel | GPU与CPU有什么不同 | CUDA | Warp

大飞说科技
28 Nov 202319:24

Summary

TLDR本期视频深入探讨了GPU的工作原理及其与CPU的区别,强调了GPU在大规模并行计算中的优势。通过比较CPU和GPU的设计目标和架构,解释了GPU如何实现高吞吐量。详细介绍了GPU的内存层次结构和执行kernel的过程,包括线程网格的配置和动态资源分区。最后,强调了编写高效GPU kernel的重要性和挑战,鼓励程序员优化代码以提高GPU资源的利用率。

Takeaways

  • 🔍 CPU和GPU的主要区别在于设计目标,CPU专注于顺序执行,而GPU专为大规模并行处理设计。
  • 🚀 GPU在深度学习等领域的广泛应用推动了其重要性的提升,特别是在执行大量线性代数和数值计算任务时。
  • 📈 与CPU相比,GPU在数值计算性能上有显著优势,如NVIDIA的Ampere A100在32位精度下的吞吐量远超Intel的24核处理器。
  • 🌟 GPU的核心组件包括流式多处理器(SM)、核心、线程、片上内存和基于硬件的线程调度器。
  • 🔧 GPU内存层次结构包括寄存器、常量缓存、共享内存、L1和L2缓存,以及片外全局内存,每一层都有特定用途。
  • 🛠️ CUDA是NVIDIA提供的编程接口,用于编写在GPU上运行的程序,其中的核心概念是kernel。
  • 🔗 在GPU上执行kernel需要数据从主机内存复制到全局内存,以及合理配置线程网格以实现并行计算。
  • 🔄 GPU通过warp执行模型实现单指令多线程(SIMT),并引入独立线程调度以提高资源利用率。
  • ⚙️ 动态资源分区和占用率是衡量GPU资源利用效率的关键指标,合理的资源分配对于保持高占用率和降低时延至关重要。
  • 📊 编写高效的GPU kernel需要仔细优化代码,平衡资源使用和性能,以达到最佳执行效率。

Q & A

  • CPU和GPU的主要设计目标有何不同?

    -CPU的设计初衷是执行顺序指令,而GPU则专门为大规模并行和高吞吐量而设计。

  • 为什么GPU在深度学习中变得重要?

    -GPU在深度学习中变得重要是因为它们能够以极高的速度执行大量线性代数和数值计算,这正是深度学习所需要的。

  • GPU的架构是如何实现高吞吐量的?

    -GPU通过使用大量流式多处理器(SM)、核心或线程、以及多层不同类型的内存来实现高吞吐量。

  • 在执行数值计算时,CPU和GPU的性能如何比较?

    -CPU在执行单个数值计算时比GPU更快,但在需要进行大量数值计算时,GPU由于其强大的并行处理能力,将比CPU更快地完成任务。

  • 什么是CUDA?

    -CUDA是NVIDIA提供的编程接口,用来编写运行在其GPU上的程序。

  • GPU中的kernel是如何定义和执行的?

    -在CUDA中,kernel是一个类似于C/C++函数的形式,用来表达在GPU上运行的计算。它在并行中操作向量形式的数字,这些数字作为函数参数提供给它。

  • 线程块和线程在GPU中的作用是什么?

    -线程块和线程是GPU执行kernel的基本单位。线程块由一个或多个线程组成,它们共同执行一个kernel函数,并且可以共享数据和同步操作。

  • warp是什么,它在GPU执行中扮演什么角色?

    -warp是GPU中一组同时执行相同指令的线程。这种执行模型称为单指令多线程(SIMT),它使得GPU能够有效地利用所有可用计算资源,提高吞吐量。

  • 动态资源分区在GPU中如何实现?

    -动态资源分区是根据线程的需求和GPU的限制,在线程之间动态划分执行资源,如寄存器、共享内存等。这种方式比固定分区更能有效地利用GPU的计算资源。

  • 如何衡量GPU资源的利用率?

    -通常使用一个称为'占用率'的指标来衡量GPU资源的利用率,它表示分配给SM的warp数量与SM所能支持的最大warp数量之间的比值。

  • 为什么GPU的占用率可能无法达到100%?

    -由于GPU的执行资源有限,如寄存器、共享内存等,以及各种约束条件,实际应用中可能无法始终达到100%的占用率。

Outlines

00:00

🖥️ CPU与GPU的比较

本段落主要介绍了CPU和GPU的基本区别和设计目标。CPU是为了执行顺序指令而设计,虽然现代CPU支持并行处理,但其主要关注点在于提高顺序执行的性能。而GPU则专注于大规模并行处理和高吞吐量,适用于需要大量线性代数和数值计算的应用场景。通过比较CPU和GPU在执行不同规模计算任务时的性能,突出了GPU在处理大规模数据时的优势。

05:03

🔍 GPU架构深入解析

这一部分详细探讨了GPU的内部架构,包括流式多处理器(SM)、核心、线程、片上内存和控制单元等组成部分。通过介绍NVIDIA H100 GPU的具体配置,说明了GPU如何通过其架构实现高吞吐量。同时,还讨论了GPU内存层次结构的不同层级,包括寄存器、常量缓存、共享内存、L1和L2缓存以及全局内存,以及它们在GPU计算中的作用。

10:05

💡 GPU Kernel执行原理

本段落深入解释了GPU是如何执行Kernel的。首先介绍了CUDA编程接口和Kernel的概念,然后详细描述了在GPU上执行Kernel所需的步骤,包括数据的准备、线程网格的配置、线程块和线程的分配以及warp的执行模型。此外,还提到了Volta及以后GPU引入的独立线程调度机制,以及warp执行中的资源利用和线程切换效率。

15:06

🔧 动态资源分区与优化

最后一部分讨论了GPU资源的动态分区和固定分区,以及如何通过优化代码来提高GPU的占用率和计算吞吐量。解释了由于资源限制导致的占用率不足问题,并强调了合理分配寄存器和共享内存等资源的重要性。通过举例说明了如何通过调整线程块大小和资源需求来优化GPU Kernel的性能。

Mindmap

Keywords

💡CPU

CPU,即中央处理器,是计算机的核心部件,负责执行程序中的指令。在视频中,CPU被提及为执行顺序指令的设备,其设计初衷是按顺序高效地执行指令,通过各种技术如指令流水线、乱序执行等减少指令执行的时延。

💡GPU

GPU,即图形处理器,是一种专门设计用于处理大规模并行计算任务的电子设备。与CPU不同,GPU擅长于执行大量相似操作,如图形渲染和深度学习中的矩阵计算。

💡并行计算

并行计算是一种计算类型,它允许同时执行多个计算任务,以提高处理速度和效率。GPU由于其设计,特别适合执行并行计算任务,因为它拥有大量的处理核心,可以同时处理多个数据点。

💡指令时延

指令时延是指执行一条指令所需的时间。CPU的设计重点在于减少这种时延,以提高顺序执行的性能。而GPU则容忍一定程度的指令时延,以实现大规模并行处理。

💡流式多处理器

流式多处理器(Streaming Multiprocessors, SM)是GPU内部的一种核心组件,负责执行并行计算任务。每个SM由多个处理单元组成,能够同时处理大量的线程和数据。

💡寄存器

寄存器是GPU中的小型、快速的存储单元,用于存储线程运行时的数据和指令。每个SM拥有大量寄存器,这些寄存器根据线程的需求进行动态分配,以支持并行计算。

💡共享内存

共享内存是GPU中的一种内存类型,由SM上的所有核心共享。它允许线程之间共享数据,从而减少从全局内存加载数据的次数,提高计算效率。

💡缓存

缓存是GPU中的高速存储区域,用于临时存储频繁访问的数据,以减少访问主内存的时间。GPU中的缓存分为多个级别,包括L1、L2缓存,以及更高级的缓存结构。

💡kernel

在GPU编程中,kernel是一个在GPU上执行的函数或程序段,它能够并行处理大量数据。编写和优化kernel是实现GPU高效计算的关键。

💡warp

warp是GPU中一组同时执行相同指令的线程。在NVIDIA的架构中,一个warp通常包含32个线程。这种设计允许GPU在执行时实现高吞吐量的并行计算。

💡占用率

占用率是衡量GPU资源利用率的一个指标,表示分配给SM的warp数量与SM所能支持的最大warp数量之间的比值。高占用率意味着更多的计算资源被有效利用,从而提高GPU的计算吞吐量。

Highlights

CPU和GPU的设计目标不同,CPU专注于顺序执行,而GPU专为大规模并行和高吞吐量设计。

GPU在深度学习等领域的广泛应用使其变得极为重要,每位软件工程师都有必要了解其基本工作原理。

CPU通过指令流水线、乱序执行、预测执行和多级缓存等功能减少指令执行的时延。

GPU通过其大规模并行能力在执行大量计算时比CPU更快,尤其在深度学习中的表现。

GPU的架构由流式多处理器(SM)组成,每个SM包含多个核心和线程,以及片上内存。

GPU内存层次结构包括寄存器、常量缓存、共享内存、L1缓存、L2缓存和全局内存。

执行GPU上的kernel需要理解CUDA编程接口,以及如何配置线程网格和线程块。

GPU执行kernel时,数据必须先从主机内存复制到GPU的全局内存。

GPU中的warp是一组同时执行相同指令的线程,这种模型也称为单指令多线程(SIMT)。

Volta及以后的GPU引入了独立线程调度,允许线程之间完全并发,不受warp的限制。

GPU通过动态资源分区来优化资源利用,根据需求和限制在线程间动态划分执行资源。

占用率是衡量GPU资源利用率的指标,表示分配给SM的warp数量与SM所能支持的最大warp数量之间的比值。

合理优化代码对保持高占用率和降低时延至关重要,以实现GPU的最佳性能。

GPU的片外全局内存通常是高带宽内存(HBM)或动态随机存取内存(DRAM),与SM相距较远,时延较高。

每个SM上都有一小块可配置的共享内存,用于线程块内线程共享数据,提高性能。

GPU上执行kernel时,线程进一步划分为大小为32的组(warp),在不同数据部分执行相同指令。

程序员需要仔细优化代码,确保执行过程中达到最高的SM占用率,以充分利用GPU的计算资源。

Transcripts

play00:00

大家好,这里是最佳拍档,我是大飞

play00:03

大多数工程师对CPU和顺序编程都十分熟悉

play00:07

这是因为自从他们开始编写基于CPU的代码以来

play00:10

就与这方面密切接触

play00:12

然而

play00:13

对于GPU的内部工作原理及其独特的地方

play00:16

他们则了解的相对较少

play00:19

过去十年

play00:20

由于GPU在深度学习中得到广泛应用而变得极为重要

play00:24

因此

play00:25

每位软件工程师都有必要了解其基本工作原理

play00:29

今天我们就来介绍一些这方面的背景知识

play00:32

首先,我们需要来比较一下CPU和GPU

play00:35

这能帮助我们更好地了解GPU的发展状况

play00:39

CPU和GPU的主要区别在于它们的设计目标

play00:43

CPU的设计初衷是执行顺序指令,当然

play00:47

得益于超线程技术和多核处理器

play00:50

CPU现在也可以并行执行任务

play00:53

但是长期以来

play00:54

大量工作都是在致力于提高顺序执行的性能

play00:58

为了提高顺序执行性能

play01:00

CPU设计中引入了许多的功能

play01:03

重点在于减少指令执行的时延

play01:06

让CPU能够尽可能快地执行一系列指令

play01:10

这些功能包括指令流水线、乱序执行、预测执行和多级缓存等等

play01:16

而GPU则专门为大规模并行和高吞吐量而设计

play01:20

但是这种设计导致了中到高程度的指令时延

play01:24

这个设计方向

play01:25

主要是受到GPU在视频游戏、图形处理、数值计算

play01:30

以及现如今的深度学习中的广泛应用所影响

play01:34

所有这些应用都需要以极高的速度执行大量线性代数和数值计算

play01:40

因此人们倾注了大量精力来提升这些设备的吞吐量

play01:44

我们来思考一个具体的例子

play01:47

由于指令时延较低

play01:48

CPU在执行两个数字相加的操作时比GPU更快

play01:53

在按顺序执行多个这样的计算时

play01:56

CPU能够比GPU更快地完成

play01:59

然而

play01:59

当需要进行数百万甚至数十亿次这样的计算时

play02:04

由于GPU具有强大的大规模并行能力

play02:07

它将比CPU更快地完成这些计算任务

play02:10

我们可以通过具体数据来进行说明

play02:13

硬件在数值计算方面的性能

play02:15

是以每秒浮点运算次数FLOPS来衡量的

play02:19

NVIDIA的Ampere A100在32位精度下的吞吐量为19.5 TFLOPS

play02:25

相比之下,2021年

play02:27

Intel的24核处理器在32位精度下的吞吐量仅为0.66 TFLOPS

play02:33

同时,随着时间推移

play02:35

GPU与CPU在吞吐量性能上的差距正在逐年扩大

play02:39

我们可以通过一个视频来直观的看一下二者的差异

play04:14

如果我们对比一下CPU和GPU的架构

play04:16

可以发现

play04:17

CPU在芯片领域中主要用于降低指令时延的功能

play04:21

例如大型缓存、较少的算术逻辑单元ALU和更多的控制单元

play04:27

与此相比

play04:28

GPU则利用大量的ALU来最大化计算能力和吞吐量

play04:33

只使用极小的芯片面积用于缓存和控制单元

play04:37

这些元件主要用于减少CPU的时延

play04:40

或许你会好奇

play04:41

GPU如何能够容忍高时延并同时提供高性能呢?

play04:46

这就要靠GPU所拥有大量线程和强大的计算能力了

play04:50

即使单个指令具有高延迟

play04:52

GPU也会有效地调度线程运行

play04:55

以便它们在任意时间点都能利用计算能力

play04:59

例如

play04:59

当某些线程正在等待指令结果的时候

play05:02

GPU将切换到运行其他非等待线程

play05:06

这可确保 GPU 上的计算单元在所有时间点

play05:10

都以其最大的容量运行

play05:11

从而提供高吞吐量

play05:13

稍后当我们讨论如何在 GPU 上运行kernel的时候

play05:17

我们将对这一点有更清晰的了解

play05:20

我们已经了解到GPU有利于实现高吞吐量

play05:23

但是它们是通过怎样的架构来实现这一目标的呢

play05:27

接下来,我们就来了解一下GPU的架构

play05:29

GPU由一系列流式多处理器

play05:32

英文名叫streaming multiprocessors

play05:35

简称SM组成

play05:36

其中每个SM又由多个流式处理器、核心或者线程组成

play05:42

例如,NVIDIA H100 GPU具有132个SM

play05:46

每个SM拥有64个核心

play05:48

总计核心高达8448个

play05:51

每个SM都拥有一定数量的片上内存

play05:54

on-chip memory

play05:56

通常称为共享内存或者临时存储器

play05:59

这些共享内存被所有的核心所共享

play06:02

同样

play06:03

SM上的控制单元资源也被所有的核心所共享

play06:07

此外

play06:07

每个SM都配备了基于硬件的线程调度器

play06:11

用来执行线程

play06:12

除此之外

play06:13

每个SM还配备了几个功能单元或其他加速计算单元

play06:18

例如张量核心(tensor core)或者光线追踪单元(ray tracing unit)

play06:23

用来满足GPU所处理的工作负载的特定计算需求

play06:27

接下来

play06:28

让我们继续深入剖析GPU的内存

play06:31

并了解其中的细节

play06:32

GPU具有多层不同类型的内存

play06:35

每一层都有它的特定用途

play06:37

如图所示是GPU中一个SM的内存层次结构

play06:41

其中包括几个部分

play06:43

首先是寄存器

play06:45

GPU中的每个SM都拥有大量寄存器

play06:48

例如,NVIDIA的A100和H100模型中

play06:52

每个SM拥有65536个寄存器

play06:55

这些寄存器在核心之间共享

play06:57

并且根据线程需求动态分配

play07:00

在执行过程中

play07:01

每个线程都被分配了私有寄存器

play07:04

其他线程无法读取或写入这些寄存器

play07:08

接下来是芯片上的常量缓存

play07:10

这些缓存用来缓存SM上执行的代码中使用的常量数据

play07:15

为了利用这些缓存

play07:16

程序员需要在代码中明确将对象声明为常量

play07:20

以便GPU可以将它们缓存并保存在常量缓存中

play07:24

每个SM还拥有一块共享内存或者临时内存

play07:28

它是一种小型、快速且低时延的片上可编程SRAM内存

play07:33

供运行在SM上的线程块共享使用

play07:36

共享内存的设计思路是

play07:38

如果多个线程需要处理相同的数据

play07:41

只需要其中一个线程从全局内存中加载

play07:44

而其他线程将共享这一数据

play07:47

合理使用共享内存可以减少从全局内存加载重复数据的操作

play07:52

并且提高内核执行性能

play07:55

共享内存还可以用作线程块内的线程之间的同步机制

play07:59

除此之外,每个SM还拥有一个L1缓存

play08:03

它可以缓存从L2缓存中频繁访问的数据

play08:06

所有SM都共享一个L2缓存

play08:09

它用于缓存全局内存中被频繁访问的数据

play08:12

从而可以降低时延

play08:14

需要注意的是

play08:15

L1和L2缓存对于SM来说是公开的

play08:20

SM并不知道它是从L1还是L2中获取数据

play08:25

SM从全局内存中获取数据

play08:27

这类似于CPU中L1/L2/L3缓存的工作方式

play08:32

GPU还拥有一个片外全局内存

play08:35

它是一种容量大且带宽高的动态随机存取存储器(DRAM)

play08:40

例如

play08:41

NVIDIA H100拥有80 GB的高带宽内存(HBM)

play08:46

带宽达每秒3000 GB

play08:48

由于与SM相距较远

play08:51

全局内存的时延相当高

play08:53

然而

play08:53

芯片上还有几个额外的存储层以及大量的计算单元

play08:57

有助于掩饰这种时延

play09:00

现在我们已经了解GPU硬件的关键组成部分

play09:03

接下来我们深入一步

play09:05

了解执行代码时这些组件是如何发挥作用的

play09:09

要理解GPU是如何执行kernel的

play09:11

我们首先需要了解什么是kernel及其配置

play09:16

首先我们要说一下CUDA

play09:18

CUDA是NVIDIA提供的编程接口

play09:20

用来编写运行在其GPU上的程序

play09:24

在CUDA中

play09:24

你会以类似于C/C++函数的形式

play09:28

来表达想要在GPU上运行的计算

play09:31

这个函数被称为kernel

play09:33

kernel在并行中操作向量形式的数字

play09:36

这些数字以函数参数的形式提供给它

play09:39

举一个简单的例子

play09:40

比如一个执行向量加法的kernel

play09:43

它会接受两个向量作为输入

play09:45

逐个元素相加

play09:46

并将结果写入第三个向量

play09:49

要在GPU上执行kernel

play09:50

我们需要启用多个线程

play09:53

这些线程总体上被称为一个网格(grid)

play09:56

但是网格还具有更多的结构

play09:59

一个网格由一个或多个线程块

play10:02

有时我们也简称为块组成

play10:04

而每个线程块又由一个或多个线程组成

play10:08

线程块和线程的数量

play10:10

取决于数据的大小和我们所需的并行度

play10:14

例如,在向量相加的示例中

play10:16

如果我们要对256维的向量进行相加运算

play10:20

那么可以配置一个包含256个线程的单个线程块

play10:25

这样每个线程就可以处理向量的一个元素

play10:28

如果数据更大

play10:29

GPU上也许没有足够的线程可用

play10:32

这时我们可能需要每个线程能够处理多个数据点

play10:36

编写一个kernel需要两步

play10:38

第一步是运行在CPU上的主机代码

play10:41

这部分代码用来加载数据

play10:43

为GPU分配内存

play10:44

并使用配置的线程网格启动kernel

play10:48

第二步是编写在GPU上执行的设备代码

play10:51

比如这个CUDA kernel的主机代码

play10:53

就是用来将两个向量相加

play10:56

而这个设备代码

play10:57

它定义了实际的kernel函数

play11:00

由于我们这期视频的重点不是讲解如何使用CUDA

play11:03

所以我们跳过这段代码

play11:05

来看看在GPU上执行kernel的具体步骤

play11:08

首先,在调度执行kernel之前

play11:11

必须将其所需的全部数据

play11:13

从主机(即CPU)内存复制到GPU的全局内存(即设备内存)

play11:18

不过在最新的GPU硬件中

play11:20

我们还可以使用统一虚拟内存

play11:23

直接从主机内存中读取数据

play11:26

接下来

play11:27

当GPU的内存中拥有全部所需的数据后

play11:31

它会将线程块分配给SM

play11:33

同一个块内的所有线程将同时由同一个SM进行处理

play11:38

为此,GPU必须在开始执行线程之前

play11:41

在SM上为这些线程预留资源

play11:45

在实际操作中

play11:46

可以将多个线程块分配给同一个SM以实现并行执行

play11:51

由于SM的数量有限

play11:53

而大型kernel可能包含大量线程块

play11:56

因此并非所有线程块都可以立即分配执行

play12:00

GPU会维护一个待分配和执行的线程块列表

play12:04

当有任何一个线程块执行完成时

play12:07

GPU会从该列表中选择一个线程块执行

play12:11

众所周知

play12:12

一个块(block)中的所有线程都会被分配到同一个SM上

play12:17

但是在此之后

play12:19

线程还会进一步划分为大小为32的组

play12:22

称为warp

play12:23

并一起分配到一个称为处理块(processing block)的核心集合上

play12:28

进行执行

play12:29

SM通过获取并向所有线程发出相同的指令

play12:34

来同时执行warp中的所有线程

play12:37

然后这些线程将在数据的不同部分

play12:40

同时执行该指令

play12:42

在向量相加的示例中

play12:43

一个warp中的所有线程可能都在执行相加指令

play12:47

但是它们会在向量的不同索引上进行操作

play12:51

由于多个线程同时执行相同的指令

play12:54

这种warp的执行模型也称为单指令多线程

play12:58

SIMT

play12:59

这类似于CPU中的单指令多数据

play13:02

SIMD指令

play13:04

Volta及其之后的新一代GPU引入了一种替代指令调度的机制

play13:08

称为独立线程调度(Independent Thread Scheduling)

play13:13

它允许线程之间完全并发

play13:15

不受warp的限制

play13:17

独立线程调度可以更好地利用执行资源

play13:19

也可以作为线程之间的同步机制

play13:22

这方面的更多内容大家可以参考一下CUDA编程指南

play13:26

关于warp的运行原理

play13:27

有一些值得讨论的有趣之处

play13:30

即使SM内的所有处理块(核心组)都在处理warp

play13:34

但是在任何给定时刻

play13:37

只有其中少数块正在积极执行指令

play13:40

因为SM中可用的执行单元数量是有限的

play13:43

有些指令的执行时间较长

play13:46

这会导致warp需要等待指令结果

play13:49

在这种情况下

play13:50

SM会将处于等待状态的warp休眠

play13:53

并执行另一个不需要等待任何结果的warp

play13:56

这使得GPU能够最大限度地利用所有可用计算资源

play14:00

并提高吞吐量

play14:02

由于每个warp中的每个线程都有自己的一组寄存器

play14:06

因此SM从执行一个warp切换到另一个warp时

play14:10

没有额外的计算开销

play14:12

与CPU上进程之间的上下文切换方式(context-switching)不同

play14:15

如果一个进程需要等待一个长时间运行的操作

play14:19

CPU在此期间

play14:20

会在该核心上调度执行另一个进程

play14:23

然而

play14:24

在CPU中进行上下文切换的代价昂贵

play14:27

这是因为CPU需要将寄存器状态保存到主内存中

play14:31

并恢复另一个进程的状态

play14:33

最后

play14:34

当kernel的所有线程都执行完毕后

play14:37

最后一步就是将结果复制回主机内存

play14:40

好了

play14:40

到这里我们已经介绍了有关典型kernel执行的全部内容

play14:45

但是还有一点值得讨论

play14:47

那就是动态资源分区

play14:49

我们通常会通过一个称为“occupancy(占用率)”的指标

play14:52

来衡量GPU资源的利用率

play14:55

它表示分配给SM的warp数量

play14:57

与SM所能支持的最大warp数量之间的比值

play15:01

为了实现最大吞吐量

play15:03

我们希望拥有100%的占用率

play15:06

然而,在实践中

play15:07

由于各种约束条件,这并不容易实现

play15:10

为什么我们无法始终达到100%的占用率呢?

play15:13

SM拥有一组固定的执行资源

play15:16

包括寄存器、共享内存、线程块槽和线程槽

play15:20

这些资源根据需求和GPU的限制

play15:23

在线程之间进行动态划分

play15:25

例如,在NVIDIA H100上

play15:28

每个SM可以处理32个线程块、64个warp

play15:32

即2048个线程

play15:34

每个线程块拥有1024个线程

play15:37

如果我们启动一个包含1024个线程的网格

play15:40

GPU将把2048个可用线程槽划分为2个线程块

play15:45

那么动态分区和固定分区相比

play15:48

动态分区能够更为有效地利用GPU的计算资源

play15:51

固定分区为每个线程块分配了固定数量的执行资源

play15:55

这种方式并不总是最有效的

play15:58

在某些情况下

play15:59

固定分区可能会导致线程被分配多于其实际需求的资源

play16:04

造成资源浪费和吞吐量降低

play16:06

我们可以通过一个例子来说明

play16:08

资源分配对SM占用率的影响

play16:11

假设我们使用32个线程的线程块

play16:13

并且需要总共2048个线程

play16:16

那么我们将需要64个这样的线程块

play16:19

然而每个SM一次只能处理32个线程块

play16:23

因此呢

play16:24

即使一个SM可以运行2,048个线程

play16:27

但是他一次也只能同时运行1,024个线程

play16:31

占用率仅仅为50%

play16:33

同样每个SM具有65536个寄存器

play16:37

要同时执行2,048个线程

play16:40

每个线程最多有32个寄存器

play16:42

因为65536除以2,048等于32

play16:46

如果一个kernel需要每个线程有64个寄存器

play16:49

那么每个SM只能运行1,024个线程

play16:53

占用率同样为50%

play16:56

如果占用率不足

play16:57

可能就无法提供足够的时延容忍度

play16:59

或者所需要的计算吞吐量

play17:01

也就难以达到硬件的最佳性能

play17:04

高效创建GPU的kernel是一项复杂的任务

play17:07

我们必须合理的分配资源

play17:09

在保持高占用率的同时尽量的降低时延

play17:13

比如说拥有大量寄存器可以加快代码的运行速度

play17:17

但是可能会降低占用率

play17:19

因此呢谨慎的优化代码至关重要

play17:22

以上呢就是对GPU架构和计算的一些入门介绍

play17:26

我们再对要点进行一下总结方便大家回顾

play17:30

首先呢GPU是由多个SM组成的

play17:33

每个SM又包含多个处理芯片

play17:36

GPU上存在着一个片外的全局内存

play17:38

通常是高带宽内存HBM

play17:41

或者是动态随机存取内存DRAM

play17:44

它与芯片上的SM相距较远

play17:46

因此时延较高

play17:48

GPU中有两个级别的缓存

play17:50

分别是片外L2缓存和片上L1缓存

play17:54

L1和L2缓存的工作方式类似于CPU中的L1、L2缓存

play17:59

每个SM上都有一小块可以配置的共享内存

play18:03

这块共享内存在处理核心之间共享

play18:06

通常情况下

play18:07

线程块内的线程会将一段数据加载到共享内存中

play18:11

并在需要的时候重复使用

play18:13

而不是每次再从全局内存中加载

play18:17

每个SM都有大量的寄存器

play18:19

寄存器会根据线程需求进行划分

play18:22

NVIDIA H100每个SM有65,536个寄存器

play18:27

在GPU上执行kernel的时候

play18:29

我们需要启动一个线程网格

play18:32

网格是由一个或者多个线程块组成的

play18:35

而每个线程块由一个或者多个线程组成

play18:38

根据资源的可用性

play18:40

GPU会分配一个或者多个线程块在SM上执行

play18:44

同一个线程块中的所有线程

play18:46

都会被分配到同一个SM上执行

play18:50

这样做的目的呢是为了充分利用数据的局部性

play18:53

并实现线程之间的同步

play18:55

被分配给SM的线程进一步的分为大小为32的组

play18:59

称为warp

play19:00

一个warp内的所有线程同时执行相同的指令

play19:04

但是在数据的不同部分上执行

play19:07

GPU会根据每个线程的需求和SM的限制

play19:10

在线程之间进行动态的资源划分

play19:13

程序员们需要仔细的优化代码

play19:15

来确保在执行过程中达到最高的SM占用率

play19:19

好了以上就是本期的全部内容

play19:21

感谢大家的观看

play19:22

我们下期再见

Rate This

5.0 / 5 (0 votes)

Related Tags
计算对比GPU架构并行处理深度学习硬件性能编程优化技术讲解工程师指南数据局部性资源分配
Do you need a summary in English?