CUDA(Compute Unified Device Architecture, 统一计算设备架构)是NVIDIA提出的并行计算架构,主要用来处理密集型及并行计算。
GPU和CPU是两个独立的处理器,通过单个计算节点中的PCI- Express总线相连,GPU用来提高计算密集型应用程序中并行程序段的执行速度,CPU则负责管理设备端的资源。
一、并行计算
并行计算通常涉及两个不同的计算机技术领域。
计算机架构(硬件方面)
并行程序设计(软件方面)
早期的计算机中,一个芯片上只有一个CPU(中央处理单元),这种结构被称为单核处理器。现在,芯片设计的趋势是将多个核心集成到一个单一的处理器上,以在体系结构级别支持并行性,这种形式通常被称为多核处理器。因此,并行程序设计可以看作是将一个问题的计算分配给可用的核心以实现并行的过程。
1.串行编程和并行编程
2.并行性
在应用程序中有两种基本的并行类型。
· 任务并行
· 数据并行
当许多任务或函数可以独立地、大规模地并行执行时,这就是任务并行。任务并行的重点是利用多核系统对任务进行分配。
当同时处理许多数据时,这就是数据并行。数据并行的重点在于利用多核系统对数据进行分配。
CUDA编程非常适合解决数据并行计算的问题。
3. 计算机架构
根据指令和数据进入CPU的方式,将计算机架构分为4种不同的类型。(弗林分类法)
SISD指的是传统计算机:一种串行架构。在这种计算机上只有一个核心。在任何时间点上只有一个指令流在处理一个数据流。
SIMD是一种并行架构类型。在这种计算机上有多个核心。在任何时间点上所有的核心只有一个指令流处理不同的数据流。向量机是一种典型的SIMD类型的计算机,现在大多数计算机都采用了SIMD架构。优势:程序员可以继续按串行逻辑思考但对并行数据操作实现并行加速,而其他细节则由编译器来负责。
MISD类架构比较少见,在这种架构中,每个核心通过使用多个指令流处理同一个数据流。
MIMD是一种并行架构,在这种架构中,多个核心使用多个指令流来异步处理多个数据流,从而实现空间上的并行性。许多MIMD架构还包括SIMD执行的子组件。
为了实现以下目的,在架构层次上已经取得了许多进展。
·降低延迟
·提高带宽
·提高吞吐量
延迟是一个操作从开始到完成所需要的时间,常用微秒来表示。带宽是单位时间内可以处理的数据量,通常表示为MB/s或GB/s。吞吐量是单位时间内成功处理的运算数量,通常表示为gflops(即每秒十亿次的浮点运算数量),特别是在重点使用浮点计算的科学计算领域经常用到。
根据内存组织方式进一步划分,可以分为两类:
· 分布式内存的多节点系统
· 共享内存的多处理器系统
在多节点系统中,大型计算引擎是由许多网络连接的处理器构成的。每个处理器有自己的本地内存,而且处理器之间可以通过网络进行通信。
多处理器架构的大小通常是双处理器到几十个或几百个处理器之间。这些处理器要么是与同一个物理内存相关联,要么共用一个低延迟的链路(如PCI-Ecpress或PCIe)。
CPU核心和CPU核心
CPU核心比较重,用来处理非常复杂的控制逻辑,以优化串行程序执行。
GPU核心比较轻,用于优化具有简单控制逻辑的数据并行任务,注意并行程序的吞吐量。
二、 异构计算
1. 异构架构
一个典型的异构计算节点包括两个多核CPU插槽和两个或更多个的众核GPU。GPU不是一个独立运行的平台而是CPU的协处理器。因此,GPU必须通过PCIe总线与基于CPU的主机相连来进行操作,CPU所在的位置被称为主机端而GPU所在的位置被称作设备端。
一个异构应用包括两个部分。
· 主机代码
· 设备代码
主机代码在CPU上运行,设备代码在GPU上运行。异构平台上执行的应用通常由CPU初始化。在设备端加载计算密集型任务之前,CPU代码负责管理设备端的环境、代码和数据。
描述GPU容量的两个重要特征:CUDA核心数量和内存大小。
评估GPU的性能的两个指标:峰值计算性能和内存带宽。
峰值性能是用来评估计算容量的一个指标,通常定义为每秒能处理的单精度或双精度浮点运算的数量。峰值性能通常用GFlops(每秒十亿次浮点运算)或TFlops(每秒万亿次浮点运算)来表示。内存带宽是从内存中读取或写入数据的比率。内存带宽通常用GB/s表示。
CPU线程与GPU线程
CPU上的线程通常是重量级的实体。上下文的切换缓慢且开销大。
GPU上的线程是高度轻量级。如果GPU必须等待一组线程执行结束,那么它只要调用另一组线程执行其他任务即可。
CPU的核被设计用来尽可能减少一个或两个线程运行时间的延迟,而GPU的核是用来处理大量并发的、轻量级的线程,以最大限度地提高吞吐量。
2. CUDA:一种异构计算平台
CUDA是一种通用的并行计算平台和编程模型。
CUDA提供了两层API来管理GPU设备和组织线程。
· CUDA驱动API
· CUDA运行时API
驱动API是一种低级API,它相对来说较难编程,但是它对于在GPU设备使用上提供了更多的控制。运行时API是一个高级API,它在驱动API的上层实现。每个运行时API函数都被分解为更多传给驱动API的基本运算。
本书中所有例子都使用运行时API。
一个CUDA程序包含了以下两个部分的混合。
· 在CPU上运行的主机代码
· 在GPU上运行的设备代码
主机代码是标准的C代码,使用C编译器进行编译。设备代码,也就是核函数,是用扩展的带有标记数据并行函数关键字的CUDA C语言编写的。设备代码通过nvcc进行编译。在链接阶段,在内核程序调用和显示GPU设备操作中添加CUDA运行时库。
三、用GPU输出Hello World
修饰符__global__告诉编译器这个函数将会从CPU中调用,然后在GPU上执行。
三重尖括号意味着从主线程到设备端代码的调用。一个内核函数通过一组线程来执行,所有线程执行相同的代码。三重尖括号里面的参数是执行配置,用来说明使用多少线程来执行内核函数。在这个例子中,有10个GPU线程被调用。
CUDA核中有3个关键抽象:线程组的层次结构,内存的层次结构以及障碍同步。