文章目录

  • 3.1 CUDA执行模型概述
  • 3.1.1 GPU架构概述
  • SM流式多处理器
  • 线程束(warp)
  • SIMT架构与SIMD架构:
  • CUDA编程对应的组件
  • 3.1.2 Fermi费米架构
  • Fermi的特征是:
  • SM
  • 片内可配置存储器
  • 并发内核执行
  • 3.1.3 Kepler架构
  • 动态并行
  • Hyper-Q技术
  • 3.1.4 配置文件驱动优化
  • CUDA提供了两个主要的性能分析工具
  • 事件和指标
  • 有3种常见的限制内核性能的因素:


3.1 CUDA执行模型概述

执行模型会提供一个操作视图,说明如何在特定的计算架构上执行指令。CUDA执行模型解释了GPU并行架构的抽象视图,有助于编写指令吞吐量和内存访问高效的代码。

3.1.1 GPU架构概述

SM流式多处理器

GPU架构围绕一个流式多处理器(SM)的可扩展阵列搭建,通过复制这种架构的构件来实现硬件并行。

  1. 每个GPU通常有多个SM,每个SM都能支持数百个线程并发执行。
  2. 当启动一个内核网格时,它的线程被分布在可用的SM上执行。线程块一旦被调度到一个SM上,其中的线程只会在那个指定的SM上并发执行;
  3. 多个线程块可能被分配到同一个SM上,且根据SM资源的可用性进行调度;同一线程中的指令利用指令级并行性进行流水化。

线程束(warp)

  1. CUDA采用单指令多线程(SIMT)架构来管理和执行线程。每32个线程为一组,被称为线程束(warp)。
  2. 线程束中所有线程同时执行相同的指令,每个线程都有自己的指令地址计数器和寄存器状态,利用自身数据执行当前指令。
  3. 每个SM都将分配给它的线程划分到包含32个线程的线程束中,然后在可用的硬件资源上调度执行。

32在CUDA程序里是一个神奇的数字,它来自于硬件系统,对软件的性能有重要影响。它是SM用SIMD方式所同时处理的工作粒度。优化工作负载以适应线程束(一组有32个线程)的边界,可以更有效地利用GPU资源。

SIMT架构与SIMD架构:

  • 相似性:两者都将相同的指令广播给多个执行单元来实现并行。
  • 区别:SIMD要求同一向量的所有元素在一个同步组中一起执行,而SIMT则允许同一线程束的多个线程独立执行
    尽管一个线程束中所有线程在相同的程序地址同时开始执行,但是单独的线程仍有可能有不同的行为。SIMT确保可以编写独立的线程级并行代码、标量线程、以及用于协调线程的数据并行代码。
  • SIMT模型包含3个SIMD所不具备的关键特征:
  1. 每个线程都有自己的指令地址计数器;
  2. 每个线程都有自己的寄存器状态;
  3. 每个线程都可以有一个独立的执行路径

CUDA编程对应的组件

  1. 一个线程块只能在一个SM上被调度,一旦线程块在一个SM上被调度,就会保存在该SM上直到执行完成。
  2. 在SM中,共享内存和寄存器是非常重要的资源。
  3. 共享内存被分配在SM上的常驻线程块中,寄存器在线程中被分配。
  4. 线程块中的线程通过这些资源可以进行相互合作和通信。
  5. 尽管线程块里的所有线程在逻辑上都可以并行运行,但在物理层面并非都能同时执行,因此线程块里的线程可能会以不同的速度前进。

下图从逻辑视图和硬件视图的角度描述了CUDA编程对应的组件。

CUDA实现深度学习 cuda simd_CUDA实现深度学习

在并行线程中共享数据会引起竞争:多个线程以不定的顺序访问同一数据,会导致不可预测的程序行为。CUDA提供了一种方法来同步线程块里的线程,以保证所有线程在进一步动作之前都达到执行过程中的一个特定点,但没有提供块间同步的原语。

  1. 尽管线程块里的线程束可以任意顺序调度,但活跃线程束的数量还是会由SM的资源所限。当线程束因任何原因闲置时,SM可以从同一SM上的常驻线程块中调度其他可用线程束。
  2. 在并发的线程束间切换没有开销,因为硬件资源已经被分配到了SM上的所有线程和块中,所以最新被调度的线程束的状态已经被存储在SM上。
  3. SM是GPU架构的核心,寄存器和共享内存是SM中的稀缺资源。CUDA将这些资源分配到SM中的所有常驻线程里,因此这些有限的资源限制了在SM上活跃的线程束数量,活跃的线程束数量对应于SM上的并行量。
  4. 了解一些SM硬件组成的基本知识,有助于组织线程和配置内核执行以获得最佳性能。

下面介绍两种GPU架构,分别是Fermi架构和Kepler架构。

3.1.2 Fermi费米架构

Fermi架构是第一个完整的GPU计算架构,能够为大多数高性能计算应用提供所需要的功能。Fermi已经被广泛应用于加速生产工作负载中。

其重点是GPU计算,它在很大程度上忽略了图形具体组成部分。

Fermi的特征是:

  1. 多达512个加速器核心,这被称为CUDA核心。
  2. 每个CUDA核心都有一个全流水线的整数算术逻辑单元(ALU)和一个浮点运算单元(FPU),在这里每个时钟周期执行一个整数或是浮点数指令。
  3. CUDA核心被组织到16个SM中,每一个SM含有32个CUDA核心。
  4. Fermi架构有6个384位的GDDR5 DRAM存储器接口,支持多达6GB的全局机载内存,这是许多应用程序关键的计算资源。
  5. 主机接口通过PCIe总线将GPU与CPU相连。
  6. GigaThread引擎(图示左侧第三部分)是一个全局调度器,用来分配线程块到SM线程束调度器上。
  7. Fermi架构包含一个耦合的768 KB的二级缓存,被16个SM所共享。

CUDA实现深度学习 cuda simd_cuda_02

SM

一个垂直矩形条表示一个SM,包含了以下内容:

  1. 执行单元(CUDA核心)
  2. 调度线程束的调度器和调度单元
  3. 共享内存、寄存器文件和一级缓存

如下图Fermi 的SM所示,

  1. 每个多处理器有16个加载/存储单元,允许每个时钟周期有16个线程(线程束的一半)计算源地址和目的地址。
  2. 特殊功能单元(SFU)执行固有指令,如正弦、余弦、平方根和插值。
  3. 每个SFU在每个时钟周期内的每个线程上执行一个固有指令。

每个SM有两个线程束调度器和两个指令调度单元。当一个线程块被指定给一个SM时,线程块中的所有线程被分成了线程束。

两个线程束调度器选择两个线程束,再把一个指令从线程束(wrap)中发送到一个组上,组里有16个CUDA核心、16个加载/存储单元或4个特殊功能单元(如图3-4所示)。

CUDA实现深度学习 cuda simd_寄存器_03

Fermi架构,计算性能2.x,可以在每个SM上同时处理48个线程束,一个线程束包括32个线程,即可在一个SM上同时常驻1536个线程。

片内可配置存储器

Fermi架构的一个关键特征是有一个64KB的片内可配置存储器,它在共享内存与一级缓存之间进行分配。

对于许多高性能的应用程序,共享内存是影响性能的一个关键因素。

共享内存允许一个块上的线程相互合作,这有利于芯片内数据的广泛重用,并大大降低了片外的通信量。

CUDA提供了一个运行时API,它可以用来调整共享内存和一级缓存的数量。根据给定的内核中共享内存或缓存的使用修改片内存储器的配置,可以提高性能。

并发内核执行

Fermi架构也支持并发内核执行:
在相同的GPU上执行相同应用程序的上下文中,同时启动多个内核。并发内核执行允许执行一些小的内核程序来充分利用GPU,如图所示。

Fermi架构允许多达16个内核同时在设备上运行。从程序员的角度看,并发内核执行使GPU表现得更像MIMD架构。

CUDA实现深度学习 cuda simd_cuda_04

3.1.3 Kepler架构

发布于2012年秋季的Kepler GPU架构是一种快速、高效、高性能的计算架构。Kepler的特点使得混合计算更容易理解。

Kepler GPU架构包含了强化的SM、动态并行、一级Hyper-Q技术三项重要创新。下图展示了Kepler K20X芯片的框图,包含了15个SM和6个64位内存控制器,提供了超过1TFlop的峰值双精度计算能力。

CUDA实现深度学习 cuda simd_CUDA_05

### 新的SM单元

其包括一些结构的创新,以提高编程效率和功率效率。

  1. 每个Kepler SM单元包含192个单精度CUDA核心,64个双精度单元,32个特殊功能单元(SFU)以及32个加载/存储单元(LD/ST)
  2. 此外还包括4个线程束调度器和8个指令调度器,以确保在单一SM上同时发送和执行4个线程束;
  3. Kepler架构的计算能力是3.5,每个SM上可同时调度64个线程束,即可常驻2048个线程;
  4. 其寄存器文件容量达到64KB;
  5. 同时还允许片内存储器在共享内存和一级缓存间有更多的分区。

CUDA实现深度学习 cuda simd_cuda_06

动态并行

动态并行是Kepler的另一新特性,它允许GPU动态启动新的网格,这样任一内核都能启动其他内核,并管理任何核间需要的依赖关系。

这一特点可以更容易地创建和优化递归以及数据相关的执行模式。下图展示了没有动态并行时主机在GPU上启动每一个内核的情况、以及有动态并行GPU能启动嵌套内核从而消除与GPU的通信需求

CUDA实现深度学习 cuda simd_cuda_07


动态并行拓宽了GPU在各种学科上的适用性。动态地启动小型和中型的并行工作负载,这在以前是需要很高代价的。

Hyper-Q技术

Hyper-Q技术增加了更多的CPU和GPU之间的同步硬件连接,以确保CPU核心能够在GPU上同时运行更多的任务。

因此,当使用Kepler GPU时,既可以增加GPU的利用率,也可以减少CPU的闲置时间

Fermi GPU依赖一个单一的硬件工作队列来从CPU到GPU间传送任务,这可能会导致一个单独的任务阻塞队列中在该任务之后的所有其他任务。
Kepler Hyper-Q消除了这个限制。如图3-9所示,Kepler GPU在主机与GPU之间提供了32个硬件工作队列。Hyper-Q保证了在GPU上有更多的并发执行,最大限度地提高了GPU的利用并提高了整体的性能。

CUDA实现深度学习 cuda simd_共享内存_08

3.1.4 配置文件驱动优化

性能分析是程序开发中的关键一步,特别是对于优化HPC应用程序代码。

配置文件驱动的发展对于CUDA编程尤为重要,原因主要有以下几个方面。

  • 一个单纯的内核应用一般不会产生最佳的性能。性能分析工具能帮助你找到代码中影响性能的关键部分,也就是性能瓶颈。
  • CUDA将SM中的计算资源在该SM中的多个常驻线程块之间进行分配。这种分配形式导致一些资源成为了性能限制者。性能分析工具能帮助我们理解计算资源是如何被利用的。
  • CUDA提供了一个硬件架构的抽象,它能够让用户控制线程并发。性能分析工具可以检测和优化,并将优化可视化。

CUDA提供了两个主要的性能分析工具

  • nvvp,独立的可视化分析器;
  • nvprof,命令行分析器。

事件和指标

在CUDA性能分析中,事件是可计算的活动,它对应一个在内核执行期间被收集的硬件计数器。指标是内核的特征,它由一个或多个事件计算得到。请记住以下概念事件和指标:

  1. 大多数计数器通过流式多处理器来报告,而不是通过整个GPU。
  2. 一个单一的运行只能获得几个计数器。有些计数器的获得是相互排斥的。多个性能分析运行往往需要获取所有相关的计数器。
  3. 由于GPU执行中的变化(如线程块和线程束调度指令),经重复运行,计数器值可能不是完全相同的。

选择合适的性能指标以及将检测性能与理论峰值性能进行对比对于寻找内核的性能瓶颈是很重要的。

有3种常见的限制内核性能的因素:

  1. 存储带宽
  2. 计算资源
  3. 指令和内存延迟