用户名  找回密码
 立即注册
注册 登录
×
热搜: 活动 交友 discuz
查看: 79|回复: 1

GPU与cuda编程

[复制链接]

1

主题

3

帖子

7

积分

新手上路

Rank: 1

积分
7
发表于 2022-12-1 16:21:54 | 显示全部楼层 |阅读模式
GPU发展历史

CUDA架构

统一的计算平台CUDA。CUDA的出现使得开发者使用GPU进行通用计算的难度大幅降低,使得开发者可以相对简单有效的对英伟达GPU芯片进行编程。



ref:
https://t.cj.sina.com.cn/articles/view/3856710564/e5e0bba4019016zc4
GPU应用




SIMT

SIMT为Single Instruction, Multiple Threads(单指令多线程技术)
SIMT相比SIMD更加灵活。在CPU中的SIMD (single Instruction, multiple data)里面,CPU一次性取出固定长度的多个数据,放到寄存器里面,用一个指令去执行。
而SIMT,可以把多条数据,交给不同的线程处理。
GPU发展趋势

计算单元
随着集成度的提高,计算单元会增加很多,算力提高很大。
显存 (DRAM)memory
DRAM是几乎所有需要在GPU上运行的数据的最初来源
GPGPU最常用的DRAM有GDDR和HBM,一般GDDR的传输率高但是位宽低,总带宽还是HBM更有优势。
显存的带宽与时延。
带宽对应单位时间内传输的数据量。
时延对应数据请求和数据返回的时间间隔。
如:
A100 80GB相对于A100 40G来说在GPU芯片上没变化,依然是A100核心,6912个CUDA核心,加速频率1.41GHz,FP32性能19.5TFLOPS,FP64性能9.7TFLOPS,INT8性能624TOPS,TDP 400W。变化的主要是显存,之前是40GB,HBM2规格的,带宽1.6TB/s,现在升级到了80GB,显存类型也变成了更先进的HBM2e,频率从2.4Gbps提升到3.2Gbps,使得带宽从1.6TB/s提升到2TB/s。
功耗
随着集成度的提高,单个晶体管的功耗降低,但是整体的晶体管数量增加,导致功耗增加。
算力与通信
有效提高算力的方法:
晶体管主要有三个用途:control,datapath(计算),存储。提高算力就要提高datapath的部分。GPU的处理过程是一个流式处理的过程,没有那么多复杂的control处理。因此相比CPU,就会去除复杂的control处理,将更多的晶体管用于算力。
并行加速:多核处理,增加核数。
增加参与运算的核数,充分利用并行算法。
提高通讯性能的方法:
减少off-chip通讯,尽量实现on-chip通信
通过缓存,减少通讯;通过缓存换取带宽,换取吞吐量。
通过压缩。换取off-chip带宽
吞吐量与带宽



吞吐量作为GPU的整体设计指标,DRAM的带宽会影响GPU的吞吐量。
高吞吐的实现:
增加核数设计实现高吞吐,减少复杂control
小缓存,换取带宽,提高高吞吐。延迟越小,吞吐越大,代价就是容量也会越小。小缓存。
算力FLOPS:
FLOPS每秒进行的浮点数运算
GPU的浮点计算:理论峰值跟CPU的计算方式基本一致
理论峰值=GPU芯片数量单个GPU核心数量GPU Boost主频*单个时钟周期内处理的浮点计算次数
如A100 80GB,包含6912个CUDA核心,加速频率1.41GHz,每个时钟内可以进行两次浮点运算,则FLOPS=69121.412=19.5 TFLOPS。
MFLOPS=10^6
GFLOPS=10^9
TFLOPS=10^12
PFLOPS=10^15
EFLOPS=10^18
ZFLOPS=10^21
ref:
https://developer.nvidia.com/gpugems/gpugems2/part-iv-general-purpose-computation-gpus-primer/chapter-29-streaming-architectures
https://mp.weixin.qq.com/s?__biz=MzI5OTIyMjQxMA==&mid=2247515324&idx=3&sn=9bbc001abbf08c944a5fec73faced4a4&chksm=ec9b2859dbeca14fc826c6a425b7c8882fb0a62fc99bd3038334335c79905e9c692cf33cccb5&mpshare=1&scene=24&srcid=1005XG5srqO7HnWGkdcfO3c0&sharer_sharetime=1664981930158&sharer_shareid=0df9e1dc808f462e2135dfa44f82e8b4&ascene=14&devicetype=android-30&version=28001c53&nettype=WIFI&abtest_cookie=AAACAA%3D%3D&lang=zh_CN&exportkey=A2TR4Fx6h4F95Z230t8nBRw%3D&pass_ticket=RKxJLziO5dCLkkp1yAQ6qT495HYqGm2bAeCLHFoN5n%2BqhiRWnyZEnd0wFw%2FwlrSa&wx_header=3 深度学习需要使用GPU的奥秘
https://zhuanlan.zhihu.com/p/375236049
CPU与GPU的基础知识

延迟和吞吐量

处理器的两个指标是经常要考虑的,为延迟和吞吐量。
延迟:是指从发出指令到最终返回结果中间经历的时间间隔。
吞吐量:指单位时间内处理的指令条数。
延迟导向与吞吐导向

ref: https://www.notion.so/CPU-GPU-2884278c29694b388360302ee1d0f388
CPU结构



(图中DRAM对应内存)
CPU结构特点:

  • 包含了多级高速的缓存结构。运算速度远高于访问存储的速度,通过空间换时间,设计了多级高速的缓存结构。将经常访问的内容放到低级缓存L1 Cache中,不经常访问的内容放到高级缓存(L2 Cache和L3 Cache)中,提升指令访问存储的速度,减少时延
  • 包含很多控制单元。主要是分支预测机制和流水线前传机制。
  • 运算单元强大:整形浮点型复杂运算速度快。
综上所述,CPU的设计导向是减少指令的时延,称为延迟导向设计。
GPU结构


(图中DRAM对应GPU的显存)
GPU结构特点

  • GPU虽有缓存结果但是数量少,要减少指令访问缓存的次数。(访问内存)
  • GPU中控制单元非常简单。控制单元中,没有分支预测和数据转发机制,复杂的指令运算就会比较慢。
  • GPU的运算单元core非常多,采用长延时流水线以实现高吞吐量。每一行运算单元的控制器只有一个,意味着每一行的运算单元使用的指令是相同的。不同的是数据内容,这种整齐划一的运算方式使得GPU对于控制简单但是运算高效的指令的效率显著增加。
GPU的设计以“增加简单指令的吞吐”为原则,称为吞吐导向设计。
CPU与GPU选择




GPU与CPU的结构,一个是吞吐导向设计,一个是延迟导向设计。
在什么情况下使用GPU和CPU呢?
在连续计算部分,延迟优先,CPU比GPU单条复杂指令延迟快10倍以上。
在并行计算部分,吞吐优先,GPU比CPU单位时间内执行指令数量10倍以上。
适合GPU的问题:
计算密集:数值计算的比例远大于内存操作,内存访问的延时可以被计算覆盖。
数据并行:大任务可以拆解为执行相同指令的小任务,对复杂流程控制的需求较低。
CUDA编程

CUDA

CUDA是英伟达推出的为GPU增加的一个易用的编程接口。
OpenCL(Open Computing Language)是异构平台并行编程的开放标准,也是一个编程框架。相比CUDA支持的平台更多,除了GPU还支持CPU、DSP、FPGA等设备。
GPU编程的基本思想和基本操作

以CUDA为例,介绍GPU编程的基本思想和基本操作。



设备端(device): 一般指GPU
一个CUDA程序包含以下三个部分:

  • 从主机host端申请device memory,把要拷贝的内容从host memory拷贝到申请的device memory里面。
  • 设备端的核函数(GPU中的核函数),对拷贝到GPU的数据进行计算,得到计算的结果。
  • 把结果从device memory拷贝到申请的host memory里面,释放内存以及设备端的显存。
CUDA编程的内存模型与软件对应




CUDA的内存模型
SP(线程处理器,流式处理器):CUDA内存模型的基本单位,包含Registers(寄存器)和local memory(局部内存)。不同的线程之间彼此独立,寄存器和局部内存只能被线程自己访问。
SM(多核处理器):由多个线程处理器和共享内存组成。多线程处理器是并行的,互不影响的。共享内存可以被多核处理器(线程块)中的所有线程访问。
GPU:有多个多核处理器和一块全局内存构成。一个GPU的所有SM共有一块全局内存,不同线程块(SM)的线程都可以使用。不同的GPU(不同的grid)则有各自的global memory。
Streaming Process Model
统一的流处理器取代GPU中原有的不同着色单元的设计,释放了GPU的计算能力。
流式编程技术简化了软件和硬件的并行处理流程。
Stream processing is a programming technique which simplifies hardware and software parallel processing.
In the stream programming model, all data is represented as a stream. 流式处理模型,以流式的方式处理句子。Kernel outputs are functions only of their kernel inputs, and within a kernel, computations on one stream element are never dependent on computations on another element.流式运算的核函数,对流式数据中的计算是完全独立的。
A CUDA-enabled GPU can have a scalable array of multithreaded streaming multiprocessors  (SMs), which is roughly equivalent to CPU cores. Each SM can have certain number of scalar processors (i.e., streaming processors, SPs) with respect to the specific architecture.
SMs为多线程流式处理器,包含多个流式处理器(SPs,标量处理器)
逻辑体系架构



线程thread对应线程处理器(SP:streaming Processor)
线程块thread block对应多核处理器 (SM: Streaming Multiprocessor)
线程块组合体(grid)对应设备端 GPU
线程块是线程的组合体:

  • 块内的线程通过共享内存、原子操作和屏障同步进行协作。
  • 不同块中的线程不能协作。
逻辑体系
kernel(核函数): thread执行的内容/代码/函数
Thread(线程):执行kernel的最小单元,调度到CUDA core中执行。
Warp (线程束):相当于Streaming Multiprocessors,用来管理调度 thread block.
thread block (线程块):多个线程的组合。被调度到SM中执行。一个SM可以同时执行多个thread block,但是一个Thread Block只能被调度到一个SM上执行。GPU中有很多Streaming Multiprocessors,用来管理调度Thread Block.
Grid:多个Thread Block的组合,被调度到整个GPU中执行。
同时,Thread、Thread Block和Grid由于所处层次不同,可以访问的存储资源不同。Thread只能访问自身的寄存器和local memory。Thread Block可以访问SM中的一级缓存(L1缓存),而Grid(多个SM)则可以访问L2缓存和更大的HBM显存。
线程束  warp



单指令多数据流机制:
每一行由一个控制单元加上若干计算单元组成,这些所有的计算单元执行的控制指令是一个。就是个非常典型的“单指令多数据流机制”。
上面这一行,称之为一个线程束warp。
多核处理器(SM)采用的是SIMT(单指令多线程)架构,warp(线程束)是最基本的执行单元。一个warp包含32个并行thread,这些线程以不同数据资源执行相同的指令。warp本质上是SM(多核处理器)在GPU上运行的最小单元。
由于warp的大小为32,所以block(线程块)所包含的thread大小一般要设置为32的倍数。
当一个kenel函数被执行时,grid(线程块组合体)中的线程块被分割到SM(多核处理器)上,一个线程块的thread(线程)只能在一个SM上调度。SM可以调度多个线程块。大量的线程thread(即grid中的不同线程块)可能被分配到不同的SM多核处理器上。
每个thread拥有自己的程序计数器和状态寄存器,并且用该线程自己的数据执行指令。就是所谓的Single Instruction Multiple Thread(SIMT)



memory features:
线程内存 thread memories:
  Data stored in register memory is visible only to the thread that wrote it and lasts only for the lifetime of that thread. 线程寄存器中的数据只对该线程可见,生命周期与线程同步。


  local memory与线程寄存器的功能相同,只是性能更慢。

多核处理器的共享内存 shared memory:
存储在共享内存内的数据,对于线程块block中的所有线程可见,生命周期与内存块同步。共享内存的存在允许线程块block中的不同线程可以通信和共享数据
ref:
https://www.microway.com/hpc-tech-tips/gpu-memory-types-performance-comparison/
https://developer.nvidia.com/gpugems/gpugems2/part-iv-general-purpose-computation-gpus-primer/chapter-29-streaming-architectures
https://smallbusiness.chron.com/importance-stream-processors-gpus-70990.html
CUDA编程函数

CUDA程序组成
一个CUDA程序包含以下三个部分:

  • 从主机host端申请device memory,把要拷贝的内容从host memory拷贝到申请的device memory里面。
  • 设备端的核函数(GPU中的核函数),对拷贝到GPU的数据进行计算,得到计算的结果。
  • 把结果从device memory拷贝到申请的host memory里面,释放内存以及设备端的显存。
CUDA程序流程:



设备端代码

  • 读写线程寄存器(线程寄存器只对该线程可见)
  • 读写Grid(线程块组合体)中全局内存
  • 读写block(线程块)中共享内存
主机端代码:

  • 申请显存、内存
  • Grid中全局内存拷贝转移(显存(GPU全局内存)、内存互相拷贝,GPU内存和CPU内存的数据转移)
  • 内存、显存释放
核函数

  • 在GPU上执行的函数
  • 一般通过标识符__global__修饰
  • 调用通过<<<参数1,参数2>>>,用于说明内核函数中的线程数量,以及线程是如何组织的。
  • 以网格Grid的形式组织,每个线程grid由若干个线程块(block)组成。每个线程块,又由若干个线程(thread)组成
  • 调用时必须声明内核函数的执行参数。
  • 编程时,必须先为kernel函数中用到的数组或者变量分配好足够的空间,再调用kernel函数,否在在GPU计算时会发生错误。
示例
两个向量相加





编译

CUDA编程执行编译的过程。
在CPU上编程,使用g++或gcc进行编译,再通过link生成可执行程序。
在GPU端,编译器就是NVCC(NVIDIA Cuda compiler driver)。

  • 逐个文件编译 (GPU 和 CPU 的程序都编译成 .o 文件。最后把它们汇总在一起,并 link 为一个可执行文件 .exe),但是这只适用于文件数较少的情况,当文件数较多时,这种办法就显得比较复杂。
  • 使用 cmake 方式编译,写一个 cmake.txt,下文有介绍。



Reference:
收藏 | CUDA 编程上手指南(一):CUDA C 编程及 GPU 基本知识
图形处理器GPU行业研究:破晓而生,踏浪前行
深度学习之GPU
深度学习需要使用GPU的奥秘
GPU深度报告,三大巨头,十四个国内玩家一文看懂 | 智东西内参 - 智东西
回复

举报

1

主题

4

帖子

7

积分

新手上路

Rank: 1

积分
7
发表于 2025-3-12 23:07:56 | 显示全部楼层
占位编辑
回复

举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋| 黑客通

GMT+8, 2025-4-13 19:55 , Processed in 0.265043 second(s), 21 queries .

Powered by Discuz! X3.4

Copyright © 2020, LianLian.

快速回复 返回顶部 返回列表