CUDA C编程(三)CUDA执行模型概述

  一般来说,执行模型会提供一个操作视图,说明如何在特定的计算架构上执行指令。CUDA执行模型揭示了GPU并行架构的GPU并行架构的抽象视图,能够提供有助于在指令吞吐量和内存访问方面编写高效代码的见解,使我们能够据此分析线程的并发。

GPU 架 构 概 述

  GPU架构是围绕一个流式多处理器(SM)的可扩展阵列搭建的。可以通过复制这种架构的构建块来实现GPU的硬件并行。下图说明了Femi SM的关键组件:CUDA核心、共享内存/一级缓存、寄存器文件、加载/存储单元、特殊功能单元、线程束调度器。其中,每一个多处理器有16个加载/存储单元,允许每个时钟周期内有16个线程(线程束的一半)计算源地址和目的地址。特殊功能单元(SFU)执行固有指令,如正弦、余弦、平方根和插值。每个SFU在每个时钟周期内的每个线程上执行一个固有指令。
CUDA C编程(三)CUDA执行模型概述
  GPU中的每一个SM都能支持数百个线程并发执行,每个GPU通常有多个SM,所以在一个GPU上并发执行数千个线程是有可能的。当启动一个内核网格时,它的线程块被分布在了可用的SM上来执行。线程块一旦被调度到一个SM上,其中的线程只会在那个指定的SM上并发执行。多个线程块可能会被分配到同一个SM上,,而且是根据SM资源的可用性进行调度的,同一线程的指令利用指令级并行性进行流水线化。
  CUDA采用单指令多线程(SIMT)架构来管理和执行线程,每32个线程为一组,被称为线程束(warp)。线程束中的所有线程同时执行相同的指令。每个线程都有自己的指令地址计数器和寄存器状态,利用自身的数据执行当前的指令。每个SM都将分配给它的线程块划分到包含32个线程的线程束中,然后在可用的硬件资源上调度执行。
  SIMT与SIMD(单指令多数据)架构类似。两者都是将相同的指令广播给多个执行单元来实现并行。一个关键的区别就是SIMD要求同一个向量中的所有元素要在一个统一的同步组一起执行,而SIMT允许属于同一线程束的多个线程独立执行。尽管一个线程束中的所有线程在相同的程序地址上同时执行,但是单独的线程仍有可能有不同的行为。SIMT确保可以编写独立的线程级并行代码、标量线程以及用于协调线程的数据并行代码。
  SIMT模型包含3个SIMD所不具备的关键特征:每个线程都有自己的指令地址技术器、每个线程都有自己的寄存器状态、每个线程都有一个独立的执行路径。
  一个线程块只能在一个SM上被调度。一旦线程块在一个SM上被调度,就会保存在该SM上直到执行完成。在同一时间,一个SM可以容纳多个线程块。下图从逻辑视图和硬件视图的角度描述了CUDA编程对应的组件。
CUDA C编程(三)CUDA执行模型概述
  在SM中,共享内存和寄存器是非常重要的资源。共享内存被分配在SM上的常驻线程块中,寄存器在线程中被分配。线程块的线程通过这些资源可以进行相互的合作和通信。尽管线程块里的所有线程都可以逻辑地并行运行,但是并不是所有线程都可以同时在物理层面执行。因此,线程块里的不同线程可能会以不同的速度前进。
  在并行线程*享数据可能会引起竞争:多个线程使用未定义的顺序访问同一个数据,从而导致不可预测的程序行为。CUDA提供了一种用来同步线程块里的线程方法,从而保证所有线程在进一步动作之前都达到执行过程中的一个特定点。然而,没有提供块间同步的原语。
  尽管线程块里的线程束可以任意顺序调度,但活跃的的线程块的数量还是会由SM的资源所限制。当线程束由于任何理由闲置的时候(如等待从设备内存中读取数值),SM可以从同一SM上的常驻线程块中调度其他可用的线程束。在并发的线程束间切换并没有开销,因为硬件资源已经被分配到了SM上的所有线程和块中,所有最新被调度的线程束的状态已经存储在SM中。
  (PS:SM是GPU架构的核心,寄存器和共享内存是SM中的稀缺资源。CUDA将这些资源分配到SM中的所有常驻线程里。因此,这些有限的资源限制了在SM上活跃的线程束数量,活跃的线程束数量对应于SM上的并行量。了解一些SM硬件组成的基本知识,有助于组织线程和配置内核执行以获得最佳的性能。)

Fermi架构

  Fermi架构是第一个完整的GPU计算架构,能够为大多数高性能计算应用提供所需要的功能,已经被广泛用于加速生产工作负载中。下图为Fermi架构的逻辑框图,其重点是GPU计算,它在很大程序上忽略了图像具体组成部分。Fermi的特征是多达512个加速器核心,这被称为CUDA核心。每个CUDA核心都有一个全流水线的整数算术逻辑单元(ALU)和一个浮点运算单元(FPU),在这里每个时钟周期执行一个整数或是浮点数指令。CUDA核心被组织到16个SM中,一个SM含有32个CUDA核心。Fermi架构有6个384位的GDDR5 DRAM存储器接口,支持多达6GB的全局机载内存,这是许多应用程序关键的计算资源。主机接口通过PCIe总线将GPU和CPU相连。GigaThread引擎是一个全局调度器,用于分配线程块到SM线程束调度器上。
CUDA C编程(三)CUDA执行模型概述
  Fermi架构包含一个耦合的768KB的二级缓存,被16个SM所共享。在下图中,一个垂直矩形条表示一个SM,包含以下内容:1.执行单元(CUDA核心);2.调度线程束的调度器和调度单元;3.共享内存、寄存器文件和一级缓存。
CUDA C编程(三)CUDA执行模型概述

  每个SM有两个线程束调度器和两个指令调度单元。当一个线程块被指定给一个SM时,线程块中的所有线程被分成为了线程束。两个线程束调度器选择两个线程束,再把一个指令从线程束发送到一个组上,组里有16个CUDA核心、16个加载/存储单元或4个特殊功能单元。Fermi架构,计算性能2.x,可以在每个SM上同时处理48个线程束,即可在一个SM上同时常驻1536个线程。
  Fermi架构的一个关键特征是有一个64KB的片内可配置存储器,它在共享内存与一级缓存之间进行分配。对于许多高性能的应用程序,共享内存是影响性能的一个关键因素。共享内存允许一个块上的线程相互合作,这有利于芯片内的广泛重用,并大大降低了片外的通信量。CUDA提供了一个运行时API,它可以用来调整共享内存和一级缓存的数量。根据给定的内核*享内存或缓存的使用修改片内存储器的配置,可以提高性能。
  Fermi架构也支持并发内核执行:在相同的GPU上执行相同应用程序的上下文中,同时启动多个内核。并发内核执行允许执行一些小的内核程序来充分利用GPU,如下图所示。Fermi架构允许多达16个内核在设备上运行,从程序员的角度看,并发内核执行使GPU表现得更像MIMD架构。
CUDA C编程(三)CUDA执行模型概述

Kepler 架 构

  Kepler GPU架构是一种快速、高效、高性能的计算架构。Kepler的特点是使得混合计算更容易理解。下图表示了Kepler K20X芯片框图,它包含了15个SM和6个64位的内存控制器。以下是Kepler架构的3个重要的创新:1.强化的SM;2.动态并行;3.Hyper-Q技术。
CUDA C编程(三)CUDA执行模型概述
  Kepler K20X的关键部分是有一个新的SM单元,其包括一些结构的创新,以提高编程效率和功率效率。每个Kepler SM单元包含192个单精度CUDA核心,64个双精度单元,32个特殊功能单元(SFU)以及32个加载/存储单元(LD/ST),如下图所示:
CUDA C编程(三)CUDA执行模型概述
  每个Kepler SM包括4个线程束调度器和8个指令调度器,以确保在单一的SM上同时发射和执行4个线程束。Kepler K20X架构(计算能力3.5)可以同时在每个SM上调度64线程束,即在一个SM上可同时常驻2048个线程。K20X架构中寄存器文件容量达到64KB,Fermi架构中只有32KB。同时,K20X还允许片内存储器在共享内存和一级缓存间有更多的分区。K20X能够提供超过1TFlop的峰值双精度计算能力,相较于Fermi的设计,功率效率提高了80%。
  动态并行是Kepler GPU的一个新特性,它允许GPU动态开启新的网格。有了这个特点,任一内核都能启动其他的内核,并且管理任何核间需要的依赖关系来正确地执行附加的工作。这一特点也使得更容易创建核优化递归与数据相关地执行模式。如下图所示,它展示了没有动态并行时主机在GPU上启动每一个内核时的情况;有了动态并行,GPU能够启动嵌套内核,消除了与CPU通信的需求。动态并行拓宽了GPU在各种学科的适用性。
CUDA C编程(三)CUDA执行模型概述

  Hyper-Q技术增加了更多的CPU和GPU之间的同步硬件连接,以确保CPU核心能够在GPU上同时运行更多的任务。因此,当使用Kepler GPU时,既可以GPU的利用率,也可以减少CPU的闲置时间。Fermi GPU依赖一个单一的硬件工作队列来从CPU到GPU间传送任务,这可能会导致一个单独的任务阻塞队列中在该任务之后的所有其他任务。Kepler Hyper-Q消除了这个限制。如下图所示,Kepler GPU在主机和GPU之间提供了32个硬件工作队列。Hyper-Q保证了在GPU上有更多的并发执行,最大限度地提高了GPU利用并提高了整体的性能。
CUDA C编程(三)CUDA执行模型概述

配 置 文 件 驱 动 优 化

  性能分析是通过检测来分析程序性能的行为:1.应用程序代码的空间(内存)或时间复杂度;2.特殊指令的使用;3.函数调用的频率和持续时间。性能分析是程序开发中的关键异步,特别是对于优化HPC应用程序代码。性能分析往往需要对平台的执行模型有一个基本的理解以指定应用程序的优化方法。开发一个HPC应用程序通常包括两个主要步骤:1.提高代码的正确性;2.提高代码的性能。对于第二步,使用配置文件驱动的方法是很自然的。配置文件驱动的发展对于CUDA编程尤为重要,原因主要有以下几个方面:1.一个单纯的内核应用一般不会产生最佳的性能。性能分析工具能帮助你找到代码中影响性能的关键部分,也就是性能瓶颈;2.CUDA将SM中的计算资源在该SM中的多个常驻线程块之间进行分配。这种分配形式导致一些资源成了性能限制者。性能分析工具能帮助我们理解计算资源是如何被利用的。3.CUDA提供了一个硬件架构的抽象,它能够让用户控制线程并发。性能分析工具可以检测和优化,并将优化可视化。

  性能分析工具深入洞察内核的性能,检测核函数中影响性能的瓶颈。CUDA提供了两个主要的性能分析工具:nvvp,独立的可视化分析器;nvprof,命令行分析器。
  nvvp是可视化分析器,它可以可视化并优化CUDA程序的性能。这个工具会显示CPU和GPU上的程序活动的时间表,从而找到可以改善性能的机会。此外,nvvp可以分析应用程序潜在瓶颈,并给出建议以消除或减少这些瓶颈。该工具即可作为一个独立的应用程序,也可以作为Nsight Eclipse Edition(nsight)的一部分。
  nvprof在命令行上收集和显示分析数据,跟nvvp一样,它可以获得CPU和GPU上CUDA关联活动的时间表,其中包含内核执行、内存传输和CUDA的API调用。它也可以获得硬件计数器和CUDA内核的性能指标。
  除了预定义的指标外,还可以利用基于分析器获得的硬件计数器来自定义指标。有三种常见的限制内核性能的因素:1.存储带宽;2.计算资源;3.指令和内存延迟。

  (PS:在CUDA性能分析中,事件是可计算的活动,它对应一个在内核执行期间被收集的硬件计数器。指标是内核的特征,它由一个或多个事件计算得到。请记住下面的概念事件和指标:1.大多数计数器通过通过流式多处理器来报告,而不是通过整个GPU,一个单一的运行只能获得几个计数器。有些计数器的获取是相互排斥的。多个性能分析运行往往需要获取所有相关的计数器。3.由于GPU执行中的变化,经重复运行,计数器值可能不是完全相同的。)

上一篇:ServletContext对象


下一篇:Spring Boot内嵌tomcat关于getServletContext().getRealPath获取得到临时路径的问题