paint-brush
CPU 和 GPU 功能的易懂高级概述经过@venkat2811
779 讀數
779 讀數

CPU 和 GPU 功能的易懂高级概述

经过 Venkat Raman15m2024/04/23
Read on Terminal Reader

太長; 讀書

本文深入探讨了 CPU 和 GPU 在处理并行计算任务方面的核心差异,涵盖了冯·诺依曼架构、超线程和指令流水线等概念。它解释了 GPU 从图形处理器演变为加速深度学习算法的强大工具的过程。
featured image - CPU 和 GPU 功能的易懂高级概述
Venkat Raman HackerNoon profile picture

对 The Die 中发生的事情进行简要概述

在本文中,我们将介绍一些基本的底层细节,以了解为什么 GPU 擅长图形、神经网络和深度学习任务,而 CPU 擅长大量连续、复杂的通用计算任务。为了写这篇文章,我必须研究几个主题,并对其进行更细致的了解,其中一些我将顺便提及。这样做是故意只关注 CPU 和 GPU 处理的绝对基础知识。


冯·诺依曼架构

早期的计算机是专用设备。硬件电路和逻辑门被编程来执行一组特定的任务。如果要完成一些新任务,就需要重新布线电路。“新任务”可能就像对两个不同的方程进行数学计算一样简单。二战期间,艾伦·图灵正在研究一种可编程机器来击败恩尼格玛密码机,后来发表了“图灵机”论文。大约在同一时间,约翰·冯·诺依曼和其他研究人员也在研究一个想法,这个想法从根本上提出了:


  • 指令和数据应该存储在共享内存(存储程序)中。
  • 处理和存储单元应该分开。
  • 控制单元负责从内存读取数据和指令,然后使用处理单元进行计算。


瓶颈

  • 处理瓶颈 - 一个处理单元(物理逻辑门)中一次只能执行一条指令及其操作数。指令按顺序依次执行。多年来,人们一直致力于制造更小的处理器、更快的时钟周期和增加内核数量,并在这方面取得了进步。
  • 内存瓶颈 - 随着处理器速度越来越快,内存和处理单元之间传输的数据量和速度成为瓶颈。内存比 CPU 慢几个数量级。多年来,人们的注意力和改进一直集中在使内存更密集、更小。


CPU

我们知道计算机中的所有东西都是二进制的。字符串、图像、视频、音频、操作系统、应用程序等都是用 1 和 0 表示的。CPU 架构(RISC、CISC 等)规范有指令集(x86、x86-64、ARM 等),CPU 制造商必须遵守这些指令集,并且这些指令集可供操作系统与硬件交互。


操作系统和应用程序(包括数据)被转换成指令集和二进制数据,以便在 CPU 中处理。在芯片级别,处理是在晶体管和逻辑门上完成的。如果您执行一个程序来将两个数字相加,则加法(“处理”)是在处理器中的逻辑门上完成的。


在按照冯·诺依曼架构设计的 CPU 中,当我们将两个数字相加时,电路中会运行一条加法指令。在那一毫秒的一小部分时间里,处理单元的(执行)核心中只执行了加法指令!这个细节总是让我着迷。


现代 CPU 中的核心

CPU 核心中的组件概览(单 HW 线程)


上图中的组件是不言而喻的。有关更多详细信息和详细解释,请参阅这篇出色的文章。在现代 CPU 中,单个物理核心可以包含多个整数 ALU、浮点 ALU 等,同样,这些单元是物理逻辑门。

为了更好地理解 GPU,我们需要了解 CPU 核心中的“硬件线程”。硬件线程是每个 CPU 时钟周期内 CPU 核心执行单元中可以完成的计算单位它代表了核心中可以执行的最小工作单元。


指令周期

在 CPU 核心上以单个硬件线程运行的进程,无需流水线或超标量


上图说明了 CPU 指令周期/机器周期。它是 CPU 执行单个指令(例如:c=a+b)的一系列步骤。


  • 提取:程序计数器(CPU 内核中的特殊寄存器)跟踪必须提取的指令。提取指令并将其存储在指令寄存器中。对于简单操作,还会提取相应的数据。

  • 解码:指令被解码以查看操作符和操作数。

  • 执行:根据指定的操作,选择并执行适当的处理单元。

  • 内存访问:如果指令很复杂或需要额外的数据(有多种因素可能导致这种情况),则在执行之前会进行内存访问。(为简单起见,在上图中忽略了这一点)。对于复杂指令,初始数据将在计算单元的数据寄存器中可用,但要完全执行指令,需要从 L1 和 L2 缓存访问数据。这意味着在计算单元执行之前可能需要等待一小段时间,并且硬件线程在等待期间仍会保留计算单元。

  • 写回:如果执行产生输出(例如:c=a+b),则输出将写回到寄存器/缓存/内存。(为简单起见,在上图或本文后面的任何地方均忽略此输出)


在上图中,只有在 t2 时刻才会进行计算。其余时间,核心处于空闲状态(没有进行任何工作)。

现代 CPU 具有硬件组件,基本上可以使(获取-解码-执行)步骤在每个时钟周期同时发生。

单个硬件线程现在可以在每个时钟周期进行计算。这称为指令流水线。


提取、解码、内存访问和写回由 CPU 中的其他组件完成。由于找不到更好的词,这些被称为“流水线线程”。当流水线线程处于指令周期的执行阶段时,它将成为硬件线程。


一个进程,在单个硬件线程中的 CPU 核心上运行,具有流水线功能


如您所见,我们从 t2 开始每个周期都会获得计算输出。以前,我们每 3 个周期获得一次计算输出。流水线提高了计算吞吐量。这是管理冯·诺依曼架构中处理瓶颈的技术之一。还有其他优化,如无序执行、分支预测、推测执行等,

超线程

这是我们结束讨论并讨论 GPU 之前我想讨论的最后一个 CPU 概念。随着时钟速度的提高,处理器也变得更快、更高效。随着应用程序(指令集)复杂性的增加,CPU 计算核心未得到充分利用,并且花费更多时间等待内存访问。


因此,我们看到了内存瓶颈。计算单元花费时间进行内存访问,而没有做任何有用的工作。内存比 CPU 慢几个数量级,而且这种差距不会很快缩小。我们的想法是增加单个 CPU 核心中某些单元的内存带宽,并在等待内存访问时让数据准备好利用计算单元。


超线程于 2002 年由英特尔在 Xeon 和 Pentium 4 处理器中推出。在超线程之前,每个核心只有一个硬件线程。使用超线程后,每个核心将有 2 个硬件线程。这是什么意思?某些寄存器、程序计数器、提取单元、解码单元等的处理电路重复。

具有 2 个 HW 线程(超线程)的 CPU 核心


上图仅显示了具有超线程的 CPU 核心中的新电路元件。这就是单个物理核心在操作系统中显示为 2 个核心的方式。如果您有一个 4 核处理器,并启用了超线程,则操作系统会将其视为 8 个核心。L1 - L3 缓存大小将增加以容纳额外的寄存器。请注意,执行单元是共享的。


两个进程,运行在具有两个硬件线程的 CPU 核心上,带有流水线


假设我们有进程 P1 和 P2 执行 a=b+c、d=e+f,由于存在硬件线程 1 和 2,因此这些可以在单个时钟周期内同时执行。如前所述,如果只有一个硬件线程,这是不可能的。在这里,我们通过添加硬件线程来增加内核中的内存带宽,以便高效利用处理单元。这提高了计算并发性。


一些有趣的场景:

  • CPU 只有一个整数 ALU。一个 HW 线程 1 或 HW 线程 2 必须等待一个时钟周期,并在下一个周期继续计算。
  • CPU 有一个整数 ALU 和一个浮点 ALU。HW 线程 1 和 HW 线程 2 可以分别使用 ALU 和 FPU 同时进行加法运算。
  • 所有可用的 ALU 均由 HW 线程 1 使用。HW 线程 2 必须等待,直到 ALU 可用。(不适用于上述加法示例,但可能适用于其他指令)。

为什么CPU在传统桌面/服务器计算方面如此出色?

  • 高时钟速度 - 高于 GPU 时钟速度。将这种高速度与指令流水线相结合,CPU 非常擅长执行顺序任务。针对延迟进行了优化。
  • 多样化的应用和计算需求 - 个人计算机和服务器具有广泛的应用和计算需求。这导致指令集复杂。CPU 必须擅长几件事。
  • 多任务和多处理 - 由于我们的计算机中有如此多的应用程序,CPU 工作负载需要上下文切换。缓存系统和内存访问就是为支持这一点而设置的。当一个进程在 CPU 硬件线程中被调度时,它已经准备好了所有必要的数据,并快速地逐一执行计算指令。

CPU 缺点

查看这篇文章并尝试Colab 笔记本。它展示了矩阵乘法如何成为可并行化的任务,以及并行计算核心如何加速计算。

  • 非常擅长顺序任务,但不擅长并行任务。
  • 复杂的指令集和复杂的内存访问模式。
  • CPU 除了计算之外,还花费大量能量进行上下文切换和控制单元活动

关键要点

  • 指令流水线提高了计算吞吐量。
  • 增加内存带宽可以提高计算并发性。
  • CPU 擅长执行顺序任务(针对延迟进行了优化)。不擅长大规模并行任务,因为它需要大量计算单元和硬件线程,而这些计算单元和线程并不可用(未针对吞吐量进行优化)。这些不可用是因为 CPU 是为通用计算而构建的,并且具有复杂的指令集。


GPU

随着计算能力的提高,对图形处理的需求也随之增加。UI 渲染和游戏等任务需要并行操作,从而推动了对电路级大量 ALU 和 FPU 的需求。为顺序任务设计的 CPU 无法有效处理这些并行工作负载。因此,GPU 的开发是为了满足图形任务对并行处理的需求,后来为它们在加速深度学习算法中的应用铺平了道路。


我强烈推荐:

  • 观看此,了解视频游戏渲染中涉及的并行任务。
  • 阅读这篇博文以了解 Transformer 中涉及的并行任务。还有其他深度学习架构,如 CNN 和 RNN。由于 LLM 正在接管世界,对 Transformer 任务所需的矩阵乘法并行性的高级理解将为本文的其余部分奠定良好的基础。(稍后,我计划全面了解 Transformer,并分享一个易于理解的高级概述,介绍小型 GPT 模型的 Transformer 层中发生的事情。)

CPU 与 GPU 规格示例

CPU 和 GPU 的核心、硬件线程、时钟速度、内存带宽和片上内存存在很大差异。例如:

  • Intel Xeon 8280 具有
    • 基本频率 2700 MHz,Turbo 频率 4000 MHz
    • 28 个核心和 56 个硬件线程
    • 总体管道线程:896 - 56
    • L3 缓存:38.5 MB(所有核心共享) L2 缓存:28.0 MB(在核心之间分配) L1 缓存:1.375 MB(在核心之间分配)
    • 寄存器大小不公开
    • 最大内存:1TB DDR4,2933 MHz,6 通道
    • 最大内存带宽:131 GB/s
    • 峰值 FP64 性能 = 4.0 GHz 2 个 AVX-512 单元每 AVX-512 单元每时钟周期 8 次操作 * 28 个核心 = ~2.8 TFLOP [使用以下公式得出:峰值 FP64 性能 = (最大 Turbo 频率) (AVX-512 单元数量) (每 AVX-512 单元每时钟周期操作次数) * (核心数量)]
      • 此数字用于与 GPU 进行比较,因为获得通用计算的峰值性能非常主观。此数字是理论上的最大限制,这意味着 FP64 电路得到了充分利用。


  • Nvidia A100 80GB SXM 具有
    • 基本频率为 1065 MHz,加速频率为 1410 MHz
    • 108 个 SM,每个 SM 有 64 个 FP32 CUDA 核心(也称为 SP),每个 SM 有 4 个 FP64 Tensor 核心,每个 SM 有 68 个硬件线程(64 + 4)
      • 每个 GPU 总计:6912 个 64 FP32 CUDA 核心、432 个 FP 64 Tensor 核心、7344 (6912 + 432) 个硬件线程
    • 每 SM 的管道线程数:2048 - 68 = 1980 个/SM
    • L2 缓存:40 MB(所有 SM 共享)L1 缓存:总共 20.3 MB(每个 SM 192 KB)
    • 寄存器大小:27.8 MB(每个 SM 256 KB)
    • 最大 GPU 主内存:80GB HBM2e,1512 MHz
    • 最大 GPU 主内存带宽:2.39 TB/s
    • 峰值 FP64 性能 = 19.5 TFLOP [仅使用所有 FP64 Tensor 核心]。当仅使用 CUDA 核心中的 FP64 时,下限值为 9.7 TFLOP。这个数字是理论上的最大限制,这意味着 FP64 电路得到了充分利用。


现代 GPU 中的核心

我们在 CPU 中看到的术语并不总是直接转化为 GPU。在这里,我们将看到 NVIDIA A100 GPU 的组件和核心。在为本文做研究时,令我感到惊讶的一件事是,CPU 供应商没有公布核心执行单元中有多少个 ALU、FPU 等。NVIDIA 对核心数量非常透明,CUDA 框架在电路级别提供了完全的灵活性和访问。


现代 CPU 与 NVIDIA GPU(来源:NVIDIA)


在上面的 GPU 图中,我们可以看到没有 L3 缓存、较小的 L2 缓存、较小但较多的控制单元和 L1 缓存以及大量的处理单元。


NVIDIA A100 GPU(来源:NVIDIA)

NVIDIA A100 GPU 流式多处理器(相当于 CPU 核心)来源:NVIDIA


以下是上图中的 GPU 组件及其与 CPU 的对应关系,以供我们初步了解。我没有做过 CUDA 编程,因此将其与 CPU 等效物进行比较有助于初步理解。CUDA 程序员非常了解这一点。

  • 多个流式多处理器<>多核 CPU
  • 流式多处理器 (SM) <> CPU 核心
  • 流处理器 (SP)/ CUDA 核心 <> CPU 核心执行单元中的 ALU / FPU
  • Tensor Core(能够在单个指令上执行 4x4 FP64 操作)<> 现代 CPU 核心中的 SIMD 执行单元(例如:AVX-512)
  • 硬件线程(在单个时钟周期内在 CUDA 或 Tensor Core 中进行计算)<> 硬件线程(在单个时钟周期内在执行单元 [ALU、FPU 等] 中进行计算)
  • HBM / VRAM / DRAM / GPU 内存 <> RAM
  • 片上存储器/SRAM (寄存器,L1,L2 缓存) <> 片上存储器/SRAM (寄存器,L1,L2,L3 缓存)
    • 注意:SM 中的寄存器明显大于核心中的寄存器。因为线程数量较多。请记住,在 CPU 的超线程中,我们看到寄存器数量有所增加,但计算单元数量没有增加。这里的原理是一样的。

移动数据和内存带宽

图形和深度学习任务需要SIM(D/T) [单指令多数据/线程] 类型执行,即根据单个指令读取和处理大量数据。


我们讨论了 CPU 中的指令流水线和超线程,GPU 也具有这种功能。其实现和工作方式略有不同,但原理相同。

GPU 调度(来源:NVIDIA)

与 CPU 不同,GPU(通过 CUDA)提供对流水线线程的直接访问(从内存中获取数据并利用内存带宽)。GPU 调度程序首先尝试填充计算单元(包括用于存储计算操作数的相关共享 L1 缓存和寄存器),然后“流水线线程”将数据提取到寄存器和 HBM 中。再次强调,CPU 应用程序程序员不会考虑这一点,并且有关“流水线线程”和每个核心的计算单元数量的规格并未公布。Nvidia 不仅公布了这些,还为程序员提供了完全的控制权。


我将在关于 CUDA 编程模型和模型服务优化技术中的“批处理”的专门文章中更详细地介绍这一点,我们可以从中看到它有多么有益。


GPU——高吞吐量处理器(来源:NVIDIA)


上图描绘了 CPU 和 GPU 核心中的硬件线程执行情况。请参阅我们之前在 CPU 流水线中讨论的“内存访问”部分。此图显示了这一点。CPU 复杂的内存管理使此等待时间足够短(几个时钟周期),以便将数据从 L1 缓存提取到寄存器。当需要从 L3 或主内存提取数据时,数据已在寄存器中的另一个线程(我们在超线程部分中看到过这一点)将获得对执行单元的控制权。


在 GPU 中,由于超额认购(大量流水线线程和寄存器)和简单的指令集,大量数据已在寄存器中等待执行。这些等待执行的流水线线程将成为硬件线程,并在每个时钟周期内执行,因为 GPU 中的流水线线程很轻量。


带宽、计算强度和延迟

超额目标是什么?

  • 充分利用每个时钟周期的硬件资源(计算单元),以最大限度地发挥 GPU 的性能。
  • 为了使计算单元保持繁忙,我们需要向其提供足够的数据。

CPU 与 GPU 上的矩阵乘法


这就是为什么在 CPU 和 GPU 中较小矩阵的矩阵乘法延迟大致相同的主要原因。尝试一下

任务需要足够并行,数据需要足够大才能使计算 FLOP 和内存带宽饱和。如果单个任务不够大,则需要打包多个这样的任务来使内存和计算饱和,以充分利用硬件。


计算强度 = FLOPs / 带宽。即计算单元每秒可完成的工作量与内存每秒可提供的数据量之比。

计算强度(来源:NVIDIA)

在上图中,我们看到,随着延迟时间的增加和带宽内存的降低,计算强度会增加。我们希望这个数字尽可能小,以便充分利用计算。为此,我们需要在 L1/寄存器中保留尽可能多的数据,以便快速进行计算。如果我们从 HBM 获取单个数据,则只有少数操作需要对单个数据执行 100 次操作才能使其值得。如果我们不执行 100 次操作,计算单元就会闲置。这就是 GPU 中大量线程和寄存器发挥作用的地方。在 L1/寄存器中保留尽可能多的数据,以保持较低的计算强度并保持并行核心繁忙。

CUDA 和 Tensor 核心之间的计算强度存在 4X 差异,因为 CUDA 核心每个时钟周期只能完成一个 1x1 FP64 MMA 指令,而 Tensor 核心每个时钟周期可以执行 4x4 FP64 MMA 指令。

关键要点

大量计算单元(CUDA 和 Tensor 核心)、大量线程和寄存器(超额认购)、精简指令集、无 L3 缓存、HBM(SRAM)、简单且高吞吐量的内存访问模式(与 CPU 相比 - 上下文切换、多层缓存、内存分页、TLB 等)是 GPU 在并行计算(图形渲染、深度学习等)方面优于 CPU 的原则


超越 GPU

GPU 最初是为了处理图形处理任务而创建的。人工智能研究人员开始利用 CUDA 及其通过 CUDA 核心直接访问强大的并行处理。NVIDIA GPU 具有纹理处理、光线追踪、光栅、多态引擎等(可以说是图形专用的指令集)。随着人工智能的采用率不断提高,擅长 4x4 矩阵计算(MMA 指令)的 Tensor 核心正在被添加,专用于深度学习。


自 2017 年以来,NVIDIA 一直在增加每个架构中的 Tensor 核心数量。但是,这些 GPU 也擅长图形处理。尽管 GPU 中的指令集和复杂性要少得多,但它并非完全专用于深度学习(尤其是 Transformer 架构)。


FlashAttention 2是针对 Transformer 架构的软件层优化(对注意层的内存访问模式的机械同情),可为任务提供 2 倍的加速。


凭借我们对 CPU 和 GPU 的深入理解,我们可以理解对 Transformer Accelerators 的需求:专用芯片(仅用于 Transformer 操作的电路),具有大量用于并行计算的计算单元、精简指令集、无 L1/L2 缓存、大量 DRAM(寄存器)取代 HBM、针对 Transformer 架构的内存访问模式优化的内存单元。毕竟 LLM 是人类的新伙伴(继网络和移动之后),它们需要专用芯片来提高效率和性能。

一些AI加速器:

变压器加速器:

基于 FPGA 的 Transformer 加速器:


参考: