当前位置: 首页 > article >正文

1、GPU 及其加速库简介

文章目录

    • 一、GPU 与 CPU 简介
      • 1、GPU 与 CPU 的区别
      • 2、GPU 分类
      • 3、GPU 优化方向
      • 4、GPU(NVIDIA A100 ) 介绍
    • 二、CUDA 简介
      • 1、多版本 CUDA 切换
      • 2、为各种 NVIDIA 架构匹配 CUDA arch 和 gencode
    • 三、OpenCL 简介
      • 1、OpenCL 平台模型
      • 2、OpenCL 执行模型
    • 四、参考资料


一、GPU 与 CPU 简介

1、GPU 与 CPU 的区别

  • GPU 的线程数(Threads)、寄存器数(Registers)、单指令多数据流(SIMD Unit)比 CPU 多,而 CPU 的缓存(Cache)及算术逻辑单元(ALU)要比 GPU 大很多
  • GPU 的功耗(显存大)相对 CPU 要大很多
  • CPU 擅长复杂逻辑控制及串行的运算,GPU 擅长的是计算密集型及并行的运算
  • GPU 加速:服务器端主要是 NVIDIA 的 GPU,使用 CUDA/cuDNN/TensorRT 加速;在 Android 手机的 GPU 领域主要是高通 Adreno 系列和 ARM Mali 系列两大类,使用 OpenCLOpenGL 或者 Vulkan 等方式进行加速
  • CPU 加速:Intel 发布了 OpenVINO 对 CPU 进行加速,ARM 使用 NEON 对 CPU 进行加速

在这里插入图片描述

2、GPU 分类

分类概述特点代表厂商
独立 GPU封装在独立的电路板,专用的显存性能高,功耗大NVIDIA/Cambricon
集成 GPU内嵌到主板上,共享系统内存性能中等,功耗中等Intel
移动端 GPU嵌在 SoC 中,共享系统内存性能低,功耗低ARM Mali/Qualcomm Adreno

3、GPU 优化方向

在这里插入图片描述

在这里插入图片描述

  • 在 NVIDIA 的 GPU 架构中,warp 是指一个由 32个线程组成的基本调度单元。每个线程束(warp)中的 32 个线程在同一个时钟周期内一起执行相同的指令,意味着它们是以 SIMT(单指令多线程数据)方式执行的
  • GPU 中的每个 SM(Streaming Multiprocessor)包含多个 warp 调度单元,每个 SM 可以在一个时钟周期内同时处理多个 warp。使用 32 个线程作为 warp 的单位能够在现代 GPU 硬件中高效地利用执行单元,避免了资源的浪费和同步问题

4、GPU(NVIDIA A100 ) 介绍

  • 一个 GPU 包含多个 Streaming Multiprocessor(SM,支持并发执行多达几百上千的 thread ) ,而每个 SM 又包含多个 CUDA Cores(全能通吃型的浮点运算单元,每一个GPU时钟执行一次值普通乘法) 和 Tensor Cores(专门为深度学习矩阵运算设计,每个GPU时钟执行一次矩阵乘法);GPU硬件上有复杂的 warp schedular 去实现多线程的 multi-threading
  • NVIDIA A100 GPU 的整体架构及算力如下图所示,GPU 频率为 1.41GHZ,处理器( Streaming Multiprocessors, SMs)数为 108,每个处理器 FP32 的 CUDA Cores 数量为 64(总CUDA 核心数:108*64=6912),那么 PeakFLOPS = 1.41*108*64*2 = 19.49TFLOPS

在这里插入图片描述在这里插入图片描述

  • 查看 GPU 的各种信息:
// 编译
nvcc gpu_info.cpp// 执行
./a.ou// 输出 GPU 信息如下
GPU Name = GeForce GTX 1080 Ti
Compute Capability = 6.1
GPU SMs = 28
GPU SM clock rate = 1.683 GHz
GPU Mem clock rate = 5.505 GHz// gpu_info.cpp 源代码如下
#include <stdio.h>
#include <stdlib.h>
#include <string.h>#include <cuda_runtime.h>#define CHECK_CUDA(x, str) \if((x) != cudaSuccess) \{ \fprintf(stderr, str); \exit(EXIT_FAILURE); \}int main(void) {int gpu_index = 0;cudaDeviceProp prop;CHECK_CUDA(cudaGetDeviceProperties(&prop, gpu_index), "cudaGetDeviceProperties error");printf("GPU Name = %s\n", prop.name);printf("Compute Capability = %d.%d\n", prop.major, prop.minor); // 获得 SM 版本printf("GPU SMs = %d\n", prop.multiProcessorCount); // 获得 SM 数目printf("GPU SM clock rate = %.3f GHz\n", prop.clockRate / 1e6); // prop.clockRate 单位为 kHz,除以 1e6 之后单位为 GHzprintf("GPU Mem clock rate = %.3f GHz\n", prop.memoryClockRate / 1e6); // 同上/*if((prop.major == 8) && (prop.minor == 0)) // SM 8.0,即 A100{// 根据公式计算峰值吞吐,其中 64、32、256、256 是从表中查到printf("-----------CUDA Core Performance------------\n");printf("FP32 Peak Performance = %.3f GFLOPS.\n", prop.multiProcessorCount * (prop.clockRate/1e6) * 64 * 2); printf("FP64 Peak Performance = %.3f GFLOPS.\n", prop.multiProcessorCount * (prop.clockRate/1e6) * 32 * 2); printf("FP16 Peak Performance = %.3f GFLOPS.\n", prop.multiProcessorCount * (prop.clockRate/1e6) * 256 * 2); printf("BF16 Peak Performance = %.3f GFLOPS.\n", prop.multiProcessorCount * (prop.clockRate/1e6) * 128 * 2);printf("INT8 Peak Performance = %.3f GOPS.\n", prop.multiProcessorCount * (prop.clockRate/1e6) * 256 * 2); printf("-----------Tensor Core Dense Performance------------\n");printf("TF32 Peak Performance = %.3f GFLOPS.\n", prop.multiProcessorCount * (prop.clockRate/1e6) * 512 * 2); printf("FP64 Peak Performance = %.3f GFLOPS.\n", prop.multiProcessorCount * (prop.clockRate/1e6) * 64 * 2); printf("FP16 Peak Performance = %.3f GFLOPS.\n", prop.multiProcessorCount * (prop.clockRate/1e6) * 1024 * 2); printf("BF16 Peak Performance = %.3f GFLOPS.\n", prop.multiProcessorCount * (prop.clockRate/1e6) * 1024 * 2); printf("INT8 Peak Performance = %.3f GOPS.\n", prop.multiProcessorCount * (prop.clockRate/1e6) * 2048 * 2); printf("INT4 Peak Performance = %.3f GOPS.\n", prop.multiProcessorCount * (prop.clockRate/1e6) * 4096 * 2);printf("INT1 Peak Performance = %.3f GOPS.\n", prop.multiProcessorCount * (prop.clockRate/1e6) * 16384 * 2);printf("-----------Tensor Core Sparse Performance------------\n");printf("TF32 Peak Performance = %.3f GFLOPS.\n", prop.multiProcessorCount * (prop.clockRate/1e6) * 1024 * 2);printf("FP16 Peak Performance = %.3f GFLOPS.\n", prop.multiProcessorCount * (prop.clockRate/1e6) * 2048 * 2);printf("BF16 Peak Performance = %.3f GFLOPS.\n", prop.multiProcessorCount * (prop.clockRate/1e6) * 2048 * 2);printf("INT8 Peak Performance = %.3f GOPS.\n", prop.multiProcessorCount * (prop.clockRate/1e6) * 4096 * 2);printf("INT4 Peak Performance = %.3f GOPS.\n", prop.multiProcessorCount * (prop.clockRate/1e6) * 8192 * 2);}*/return 0;
}// CUDA 运行时头文件
#include <cuda_runtime.h>// CUDA 驱动头文件
#include <cuda.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>#define LOG(...) __log_info(__VA_ARGS__)// 使用变参进行LOG的打印。比较推荐的打印log的写法
static void __log_info(const char *format, ...)
{char msg[1000];va_list args;va_start(args, format);vsnprintf(msg, sizeof(msg), format, args);fprintf(stdout, "%s\n", msg);va_end(args);
}int main()
{int count;int index = 0;cudaGetDeviceCount(&count);while (index < count){cudaSetDevice(index);cudaDeviceProp prop;cudaGetDeviceProperties(&prop, index);LOG("%-40s", "*********************Architecture related**********************");LOG("%-40s%d%s", "Device id: ", index, "");LOG("%-40s%s%s", "Device name: ", prop.name, "");LOG("%-40s%.1f%s", "Device compute capability: ", prop.major + (float)prop.minor / 10, "");LOG("%-40s%.2f%s", "GPU global meory size: ", (float)prop.totalGlobalMem / (1 << 30), "GB");LOG("%-40s%.2f%s", "L2 cache size: ", (float)prop.l2CacheSize / (1 << 20), "MB");LOG("%-40s%.2f%s", "Shared memory per block: ", (float)prop.sharedMemPerBlock / (1 << 10), "KB");LOG("%-40s%.2f%s", "Shared memory per SM: ", (float)prop.sharedMemPerMultiprocessor / (1 << 10), "KB");LOG("%-40s%.2f%s", "Device clock rate: ", prop.clockRate * 1E-6, "GHz");LOG("%-40s%.2f%s", "Device memory clock rate: ", prop.memoryClockRate * 1E-6, "Ghz");LOG("%-40s%d%s", "Number of SM: ", prop.multiProcessorCount, "");LOG("%-40s%d%s", "Warp size: ", prop.warpSize, "");LOG("%-40s", "*********************Parameter related************************");LOG("%-40s%d%s", "Max block numbers: ", prop.maxBlocksPerMultiProcessor, "");LOG("%-40s%d%s", "Max threads per block: ", prop.maxThreadsPerBlock, "");LOG("%-40s%d:%d:%d%s", "Max block dimension size:", prop.maxThreadsDim[0], prop.maxThreadsDim[1], prop.maxThreadsDim[2], "");LOG("%-40s%d:%d:%d%s", "Max grid dimension size: ", prop.maxGridSize[0], prop.maxGridSize[1], prop.maxGridSize[2], "");index++;printf("\n");}return 0;
}// 输出如下
*********************Architecture related**********************
Device id:                              0
Device name:                            NVIDIA GeForce GTX 1080
Device compute capability:              6.1
GPU global meory size:                  7.92GB
L2 cache size:                          2.00MB
Shared memory per block:                48.00KB
Shared memory per SM:                   96.00KB
Device clock rate:                      1.83GHz
Device memory clock rate:               5.00Ghz
Number of SM:                           20
Warp size:                              32
*********************Parameter related************************
Max block numbers:                      32
Max threads per block:                  1024
Max block dimension size:               1024:1024:64
Max grid dimension size:                2147483647:65535:65535

在这里插入图片描述


二、CUDA 简介

  • CUDA(Compute Unified Device Architecture):是一种由 NVIDIA 推出的通用并行计算架构,该架构使 GPU 能够解决复杂的计算问题;,它包括编译器(nvcc)、开发工具、运行时库和驱动等模块,是当今最流行的GPU编程环境
  • cuDNN:是基于 CUDA 的深度学习 GPU 加速库,支持常见的深度学习计算类型(卷积、下采样、非线性、Softmax 等)
  • 一个基本的 CUDA 程序架构包含 5 个主要方面:
    • 分配 GPU 内存
    • 复制 CPU内存数据到 GPU 内存
    • 激活 CUDA 内核去执行特定程序的计算
    • 将数据从 GPU 拷贝 到 CPU 中
    • 删除 GPU 中的数据

1、多版本 CUDA 切换

# 1、查看当前 cuda 版本
nvcc -V 
nvcc: NVIDIA (R) Cuda compiler driver
Copyright (c) 2005-2017 NVIDIA Corporation
Built on Fri_Sep__1_21:08:03_CDT_2017
Cuda compilation tools, release 9.0, V9.0.176# 2、删除之前创建的软链接
sudo rm -rf /usr/local/cuda# 3、建立新的软链接,cuda9.0 切换到 cuda11.0
sudo ln -s /usr/local/cuda-11.0/ /usr/local/cuda/# 4、查看当前 cuda 版本
nvcc -V 
nvcc: NVIDIA (R) Cuda compiler driver
Copyright (c) 2005-2020 NVIDIA Corporation
Built on Wed_Jul_22_19:09:09_PDT_2020
Cuda compilation tools, release 11.0, V11.0.221
Build cuda_11.0_bu.TC445_37.28845127_0# 5、将 ~/.bashrc 或 caffe Makefile.config 等下与 cuda 相关的路径都改为 /usr/local/cuda/(指定版本的软链接)
vim ~/.bashrc  # 不使用 /usr/local/cuda-9.0/ 或 /usr/local/cuda-11.0/ 等,这样每次切换版本,就不用改配置了export PATH=$PATH:/usr/local/cuda/bin
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/cuda/lib64:/usr/lib/x86_64-linux-gnu
export LIBRARY_PATH=$LIBRARY_PATH:/usr/local/cuda/lib64source ~/.bashrc  # 立即生效

2、为各种 NVIDIA 架构匹配 CUDA arch 和 gencode

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

  • Nvidia 通用计算 GPU 架构历程如下表所示:Volta(推出第一代 tensor core) -> Turning(第二代 tensor core) -> Ampere(第三代 tensor core)
  • CUDA C++ 编译器 nvcc 基于每个内核,既可以用来产生特定于体系结构的 cubin 文件(sm_XX),又能产生前向兼容的 PTX 版本(compute_XX),使用较新的架构的 GPU 设备能够向下兼容在旧架构设备上编译得到的可执行程序
  • CUDA 代码编译的时候,需要根据 GPU 所使用的架构提供 archgencode,不同设备的编译选项说明可参考下面代码示例
# 一、各种架构下 GPU arch 和 gencode 的设置说明
- gencode:生成码,允许生成更多的 PTX 文件,并且对不同的架构可以重复许多次
- arch:架构标志位,前端编译目标,指明了 CUDA 文件编译产生的结果所依赖的 NVIDIA GPU 架构的名称,必须始终为 PTX 版本
- compute_XX:指的是 PTX 版本
- sm_XX:指的是 cubin 版本
- code:指定后端编译目标,可以是 cubin 或 PTX 或两者均可# 1、Maxwell cards(CUDA 6 until CUDA 11):GTX Titan X, GTX-970, GTX-980
ARCH= -gencode arch=compute_52,code=[sm_52,compute_52]# 2、Pascal (CUDA 8 and later):GTX 1080, Titan Xp, Tesla P40, Tesla P4
ARCH= -gencode arch=compute_61,code=[sm_61,compute_61]
ARCH= -gencode arch=compute_60,code=[sm_60,compute_60]  # Quadro GP100, Tesla P100, DGX-1 (Generic Pascal)# 3、Volta (CUDA 9 and later):Tesla V100, GTX 1180 (GV104), Titan V
ARCH= -gencode arch=compute_70,code=[sm_70,compute_70]# 4、Turing (CUDA 10 and later):GeForce RTX 2080 Ti, RTX 2080, Quadro RTX 8000, Tesla T4
ARCH= -gencode arch=compute_75,code=[sm_75,compute_75]# 5、Ampere (CUDA 11.1 and later)
ARCH= -gencode arch=compute_80,code=[sm_80,compute_80]  # NVIDIA A100, NVIDIA DGX-A100
ARCH= -gencode arch=compute_86,code=[sm_86,compute_86]  # Tesla GA10x cards, RTX 3090/3080, NVIDIA A40,  Quadro A10, Quadro A16, Quadro A40# 6、CAFFE CUDA architecture setting: going with all of them.
ARCH= -gencode arch=compute_50,code=[sm_50,compute_50] \-gencode arch=compute_52,code=[sm_52,compute_52] \-gencode arch=compute_61,code=[sm_61,compute_61] \-gencode arch=compute_70,code=[sm_70,compute_70]---------------------------------------------------
# 二、Sample nvcc gencode and arch Flags in GCC 
# 1.Sample flags for generation on CUDA 9.2 for maximum compatibility with Volta cards:
-arch=sm_50 \
-gencode=arch=compute_50,code=sm_50 \
-gencode=arch=compute_52,code=sm_52 \
-gencode=arch=compute_60,code=sm_60 \
-gencode=arch=compute_61,code=sm_61 \
-gencode=arch=compute_70,code=sm_70 \ 
-gencode=arch=compute_70,code=compute_70# 2.Sample flags for generation on CUDA 10.1 for maximum compatibility with V100 and T4 Turing cards:
-arch=sm_50 \ 
-gencode=arch=compute_50,code=sm_50 \ 
-gencode=arch=compute_52,code=sm_52 \ 
-gencode=arch=compute_60,code=sm_60 \ 
-gencode=arch=compute_61,code=sm_61 \ 
-gencode=arch=compute_70,code=sm_70 \ 
-gencode=arch=compute_75,code=sm_75 \
-gencode=arch=compute_75,code=compute_75 # 3.Sample flags for generation on CUDA 11.0 for maximum compatibility with V100 and T4 Turing cards:
-arch=sm_52 \ 
-gencode=arch=compute_52,code=sm_52 \ 
-gencode=arch=compute_60,code=sm_60 \ 
-gencode=arch=compute_61,code=sm_61 \ 
-gencode=arch=compute_70,code=sm_70 \ 
-gencode=arch=compute_75,code=sm_75 \
-gencode=arch=compute_80,code=sm_80 \
-gencode=arch=compute_80,code=compute_80# 4.Sample flags for generation on CUDA 11.0 for maximum compatibility with V100 and T4 Turing cards, but also support newer RTX 3080 and other Ampere cards:
-arch=sm_52 \ 
-gencode=arch=compute_52,code=sm_52 \ 
-gencode=arch=compute_60,code=sm_60 \ 
-gencode=arch=compute_61,code=sm_61 \ 
-gencode=arch=compute_70,code=sm_70 \ 
-gencode=arch=compute_75,code=sm_75 \
-gencode=arch=compute_80,code=sm_80 \
-gencode=arch=compute_86,code=sm_86 \
-gencode=arch=compute_86,code=compute_86# 5.Sample flags for generation on CUDA 11.1 for best performance with RTX 3080 cards:
-arch=sm_80 \ 
-gencode=arch=compute_80,code=sm_80 \
-gencode=arch=compute_86,code=sm_86 \
-gencode=arch=compute_86,code=compute_86 # 三、Using Cmake for TensorRT: drop the sm_ and compute_ prefixes, refer only to the compute capabilities instead
# 1.Example for Tesla V100 and Volta cards in general:
cmake <...> -DGPU_ARCHS="70"# 2.Example for NVIDIA RTX 2070 and Tesla T4:
cmake <...> -DGPU_ARCHS="75"# 3.Example for NVIDIA A100:
cmake <...> -DGPU_ARCHS="80"# 4.Example for NVIDIA RTX 3080 and A100 together:
cmake <...> -DGPU_ARCHS="80 86"

三、OpenCL 简介

异构多核处理器:一般指在CPU里集成了CPU与其他模块(GPU/DSP/FPGA)一起同步计算,因为是属于不同的设备但要一同工作,所以需要设计可以让他们协同工作。海思Hi3559A芯片架构如下:
在这里插入图片描述

1、OpenCL 平台模型

  • Opencl的平台模型是由一个主机和若干个设备组成,也就是一个Host+多个Device的组织形式。这些设备可以是CPU、GPU、DSP、FPGA等。这种多种处理器混合的结构,就组成了异构并行计算平台。在这些Device中又包含了一个或者多个计算单元(Computing Units, CU),每个计算单元中可以包括若干个处理元件(Processing Elements, PE),内核程序(kernel)最终就在各个PE上并行运行。
    在这里插入图片描述

2、OpenCL 执行模型

  • 理解执行模型对于理解OpenCl的程序至关重要,所谓执行模型是指OpenCL程序的运行方式,OpenCL的程序包括Host程序和Kernel程序两部分。Host程序运行在主机上,Kernel程序的执行依赖于Host程序中定义的上下文。OpenCL的执行模型的核心理念就是通过主机管理运行在Kernel上的程序(一般OpenCL代码中包含多个kernel程序)。
  • 由于OpenCL是数据并行的,同一时刻会在PE上同时执行同一条kernel函数,只是数据不同而已。相同时刻同时执行一条kernel,怎么区分哪个PE上跑的是什么数据?OpenCL会为每个PE执行的kernel分配index, 用于区分相同函数不同的数据,为OpenCL抽象的最小可执行单位被称为工作节点(Work Item),每个kernel执行时都会分配一个全局的工作节点 index。
  • 每个Kernel程序运行在一个工作节点上(Work Item),同时有一个索引与之对应,通过这个索引建立了Kernel程序和它所要处理的数据之间的关系。程序申请的所有处理节点构成的组合就称之为工作空间。换句话说,工作空间就是当前程序所需要的所有处理节点的集合,它可以是一维、二维和三维的,每一个work item都有一个全局的ID,称为global ID。对应的每一个运行在处理节点上的kernel程序都可以通过一个全局索引访问。
  • 除了全局的工作空间外,opencl还提供了将工作空间进一步划分为工作组空间(work group)的结构,这就是工作组的由来。工作组的维度必须和工作空间的维度相同,并且共组的每个维度都可以整除工作空间的每个对应的维度。这样一来,每个工作组都有一个work group ID,每个工作组内,有多个work item,每个work item又对应有一个局部的ID,称为local ID。
  • Opencl 将相同的kernel下的work-item,以相同数量的划分为work-group。也就是相同kernel下的work-group之间的work-item是相同的。opencl能够保证同一条 work-group下的work-item是并行的,但是不同的work-group下的不同work-item并不能保证并行,所以opencl只能够支持同一work-group的work-item的同步。其次work-item大小的划分是和硬件相关的,每个芯片不一样。work-item和work-group可以和硬件很好的映射起来,这是opencl抽象模型的优势之一, work-item可以映射为GPU的PE, 每个GPU有多个CU,在同一个CU下面有多个PE。在CU里面有多个调度器,可以保证在该CU下的PE能够并行运行,故可以将work-item映射为PE, work-group映射为CU
  • 注意:
    1.每个GPU都有固定的处理单元,如何调度这些单元工作是程序优化的关键;
    2.工作空间和工作组空间每个维度上的整除关系一定要注意。

四、参考资料

1、CUDA架构及对应编译参数
2、http://arnon.dk/matching-sm-architectures-arch-and-gencode-for-various-nvidia-cards/
3、https://en.wikipedia.org/wiki/CUDA#GPUs_supported
4、GPU 优化技术-OpenCL 介绍
5、GPU 优化技术-OpenCL 运行时 API 介绍
6、腾讯:这篇GPU学习笔记,详细整理了其工作原理、编程模型和架构设计

http://www.lryc.cn/news/2421207.html

相关文章:

  • Jbpm工作流介绍
  • 360安全卫士hookport.sys简单逆向
  • vba宏语言_Excel VBA(1) – VBA 简介及录制宏
  • js 汉字转化拼音
  • informix安装教程以及创建一个实例(详细)(系列1,informix安装篇)
  • 牛客13611题解
  • 安装ADOBE READER时无法将数值写入键
  • 【css js vue】超详细!!实现 tab菜单 动态滑动效果
  • 手动安装msu补丁
  • 有道词典的本地/扩展/离线词库
  • Ubuntu 彻底卸载 Opera 浏览器
  • PQ硬盘分区魔术师怎么用|PQ8.05硬盘分区教程图解
  • 安凯anycloud39EV330开发环境搭建
  • [转]Flixel只能用于Flex吗?
  • 【笔记】使用nvm install下载显示淘宝镜像证书过期
  • [新版新概念英语1-4册全部视频和课本]
  • js动态添加和移除disabled属性和style
  • 基于 Spark 的数据分析香港六合彩开奖号码采集官网实践
  • clannad手游汉化版_clannad游戏中文版
  • mysql查询关键词总结
  • 6.2路由器与交换机的作用与特点
  • VMware View 5.0 – 远程图形工作站配置篇
  • 用计算机测试生日,超准生日爱情配对测试
  • 最全、最详细的MySQL常用命令(MySQL)
  • 干式真空泵原理_如何安装干式墙锚在墙壁上悬挂重物
  • 【转】2009年值得去关注的15个国内web2.0网站
  • 赢销侠的秘密武器:如何通过创新思维提升业绩
  • [喵咪开源软件推荐(5)]开源DNS服务-bind
  • Linux - tftp
  • 【Windows源码分析】(一)初始化内核与执行体子系统