点击下方卡片,关注“自动驾驶之心”公众号
ADAS巨卷干货,即可获取
作者 | 王汝嘉 许金浩
编辑 | 自动驾驶之心
并行处理与GPU体系架构
笔记:王汝嘉 许金浩
课程讲师:韩博
课程链接:CUDA与TensorRT部署实战课程 (zdjszx.com)
引言:
大家使用TensorRT进行部署的时候,仅使用TensorRT的API将其转化为推理引擎(engine)跑在GPU上是不OK的。在部署的过程中,我们要考虑的事情有很多,包括模型和推理引擎有没有充分去利用GPU资源,假如你的CUDA core和Tensor core有很多,有没有把这些核全部给利用起来呢?你的模型的计算密度是多少呢?有没有在利用一些计算密度很低的模块,让CUDA core和Tensor core没有充分利用起来呢?你的模型的可并行性是多少呢?是不是有很多计算并没有充分使用GPU资源做并行化呢?等等。所以并行处理和GPU体系架构作为一个基础来讲解。
火热开课!扫码领取优惠券!
主要内容:
并行处理简介
GPU并行处理
CUDA,CUDDNN,TENSOR,NEOVIM环境搭建
并行处理简介
Goal:理解并行处理的基本概念,理解SIMD,以及 (C/C++) 编程中常见的并行处理方式
串行处理与并行处理的区别
Sequential processing(串行)
指令/代码块 依次执行,前一条指令执行结束以后才能执行下一条语句
一般来说,当程序有数据依赖或者分支等这些情况下需要串行
1.数据依赖:
2.分支:
PS:data dependency的种类
- Flow dependency(上面的例子)
当一条指令依赖于前一条指令的结果时,就会出现Flow依赖,也称为数据依赖或真正的依赖或read-after-write(RAW)。
- Anti dependency
反依赖,也称为(write-after-read, WAR),发生在指令要求稍后更新的值时。
- Output dependency
当指令的顺序将影响变量的最终输出值时,就会发生输出依赖,也称为write-after-write(WAW)。
- Control dependency
如果指令B的结果决定了是否执行指令B,那么指令B对前面的指令a有控制依赖。
更多可参见:Data dependency - Wikipedia
Sequential processing(串行)
使用场景:复杂的逻辑计算(比如:操作系统)
示例:
前提条件:
statement 2 依赖 statement 1
statement 5 依赖 statement 4
statement 3 是一个 循环,跟所有的statement没有任何关系
有四个core可以使用
时钟周期 (执行时间) :
statement 1, statement 2: 5 cycles
statement 4, statement 5: 8 cycles
statement 3: 20 cycles
如果串行的话,需要花费46个cycles,太慢了。
既然我们有四个Core,那么我们为什么不用这四个Core来进行并行运算呢?
并行计算:
简单的并行处理之后,时间花费变为了20 cycles,同时我们满足了程序的执行依赖,不会改变程序的执行结果。
statement3这个FOR循环还有什么可以优化的吗?
假设给定条件:
statement3的循环可以分割成多个子代码执行(每个子代码快 5cycles)
那么我们就可以将这个大的Statement3 FOR循环,拆分为4个小的子代码块来并行处理。
因为我们有4个Core,剩余一个Core没有使用,所以我们将这4个子代码块再分为两个部分,如下:
(默认拆分时可以满足依赖关系)
拆分FOR循环之后,时间花费变为了16 cycles!
再加一个依赖条件:
statement 5 可以在 statement 4 彻底执行结束前就知道所依赖的结果了:
也就是当statement4还未执行结束时,假设执行到了3/4时,就已经计算得出了statement 5所需要的依赖,
那么我们就可以让statement5和statement4的执行重叠,这种方法叫做'overlap',如下:
到此我们就将时间花费通过并行和拆分的方式减少到了14 cycles.
但是上述并行不是用了5个Core吗?
回顾一下,我们在这里都做了哪些事情:
把没有数据依赖的代码分配到各个core各自执行 (schedule, 调度)
把一个大的loop循环给分割成多个小代码,分配到各个core执行(loop optimization)
在一个指令彻底执行完以前,如果已经得到了想要得到的数据,可以提前执行下一个指令(pipeling, 流水线)
我们管这一系列的行为,称作parallelization (并行化)。我们得到的可以充分利用多核多线程的程序叫做**parallelized program(**并行程序)
并行处理的概念
Parallel processing(并行)
- 指令/代码块同时执行
- 充分利用multi-core(多核)的特性,多个core一起去完成一个或多个任务
- 使用场景:大规模科学计算(如天体预测),图像处理,深度学习等等
假设我们的机器有四个Core
多核机器完成一个任务:我们可以让这四个Core一起去执行FOR循环,我们把一个FOR循环分割,分割成一个一个子代码块,让每一个Core去执行一个子代码块
多核机器完成多个任务:假设一个程序很大,它有很多个任务,我们可以让一个Core去完成一个任务,每个Core任务完成之后,我们在程序结尾的时候做一个同步处理 。
多核处理器架构:
硬件:
Main Memory: 主存
L3 Cache: L3 缓存
L2 Cache: L2 缓存
i-Cache(instruction cache):指令缓存
d-Cache (data cache):数据缓存
Core: 核
软件:
Host Operating System: 操作系统
程序:
APP 1: 程序1
...
Parallel processing(并行)
Loop parallelization(循环并行化)
resize, crop, blur(模糊处理), bgr2rgb, rgb2gray, dbscan(轮廓检测), findCounters(轮廓检测)
大部分消耗时间长的程序中,要不然就是在I/O上的内存读写消耗时间上长,要不然就是在**loop(循环)**上。针对loop的并行优化是很重要的一个优化策略!
在图像处理/深度学习中很多地方都是用到了循环
比如说:pre/post process (前处理后处理)
在比如说:DNN中的卷积(convolution layer)以及全连接层(Fully connected layer)
下面以4*4的二维矩阵进行举例:
Default ordering (默认遍历顺序): row-major
Reordering (重排序):
比如对于Fortan语言,其存储方式是column major的,假设代码里写的是row major的遍历方式,我们就可以将row major的遍历顺序改为column major的遍历方式。(代码遍历方式==存储遍历方式)
这样可以减少Cache Miss,具体来说:我们在CPU程序优化中,要尽量减少程序的Cache Miss。在存取数据的时候,要考虑它的空间连续性和时间连续性,比如说一个元素被访问到之后,其旁边的元素也有很大的机率被访问,所以把这一列的数据放在一起,这样再去Cache里访问的时候,就可以降低Cache Miss。
(*)请注意,不同编程语言的默认的存储ordering是不同的。这里举几个例子:
row major: C/C++/Objective-C,Pascal,C# (行优先)
column major: Fortran, OpenGL, MATLAB,R,Julia (列优先)
所以不同语言的reordering效果会不同。这里建议大家测试一下
Picture From:Row and column major order - Row- and column-major order - Wikipedia
Vectorization:标量化:
默认情况下是一个元素一个元素进行访问的,
我们可以四个元素四个元素一起访问,这样会提高吞吐量。
注意,标量化需要有硬件支持。
Tiling(分块):
假如说我们有一个16 * 16的数组,那么我们将其分为四个4 * 4的数组,那么我们就可以对每一个小数组进行独立的遍历。
也就是说,这四个小部分的计算和遍历,其实是并行的。
以上是Loop parallelization中几个较为常用的方法,还有很多拓展方法,可以自己探索。后面也会细致的讲解。
Loop Parallelization不仅仅是在CPU的程序优化中使用到,其实在GPU的优化中也完美的体现了。
Parallel processing(并行)
Loop parallelization
双线性插值:用于resize,用四个点(Q12,Q22,Q11,Q21)的坐标值来计算中间点坐标值。每一个计算和旁边的计算是没有依赖关系的。
所以每一个计算是可以并行的。
卷积层:每一个卷积核的计算也是没有依赖关系的,所以每个卷积核的计算也是可以并行的。
全连接:利用9个输入计算输出A,B,C,D的四个计算过程也是相互独立的,所以也可以并行计算。
PS:容易混淆的几个概念
“并行”与“并发”的区别
并行(parallel)
物理意义上同时执行
并发(concurrent)
逻辑意义上同时执行
对于并发:
一个CPU要执行两个任务,先执行Task1,执行一半后,执行Task2,执行Task2一半后,执行Task1,执行完Task1,执行Task2,Task1和Task2都执行结束。
对于并行:
有两个GPU,分别要执行两个不同的任务,两个GPU同时工作。
本课程基于并行来讲,GPU CUDA用并行多。
并发对于编译器优化较多。
“进程”与“线程”的关系
线程是进程的子集。一个进程可以有多个线程
举例:操作系统中打开一个小程序,系统默认为该小程序分配一个线程,这个小程序的打开非常慢,所以系统可以为其分配多个线程来分别完成小程序中的各个部分,以此来打开这个小程序。打开这个小程序的过程就是进程。
“多核”与“加速比”的关系
双核的加速不一定就是两倍
8核的加速比有时会差于4核
1 举例:单核处理一个程序需要4ms,双核处理一个同样程序则就需要2ms吗?
答案是错误的,需要的时间往往大于2ms.
2 举例:有时候核数越多,加速效果也不一定越好。因为核数越多,内存开销等也会增大,也会消耗时间。
还可参见:Speedup - Wikipedia
横轴:Number of processors 处理器个数(核数)
纵轴:Speedup 加速比
我们会发现,随着处理器个数 (核数) 的增加,加速比会降低,到达一定程度之后,加速比会饱和。
why: 程度的进程以及线程之间会有依赖关系,所以会有不能并行和不能完全并行的情况存在,所以就做不到理想的加速比保持。
常见的并行处理的概念
自古以来的非常非常热门的课题
• 编译器自动化并行优化
TVM:针对深度神经网络DNN的编译器
• GCC, LLVM, TVM, …
• 针对For循环的并行优化
• tile, fuse, split, vectorization, …
• 计算图优化
和pytorch和tensorflow中的计算图不同,其可以理解为是程序的流程图。
针对程序的流程图进行优化。
• CFG, HTG, MTG, …
• 数据流
• Dataflow compiler
• Dataflow architecture
• Polyhedral
• Polyhedral compiler
• HPC
• High performance computing
• …
TensorRT也可以理解成是一个编译器,其输入模型,由TensorRT做各种优化后,输出推理引擎,来让模型在硬件上最优化程序运行时间。
SIMD
Single Instruction Multiple Data (同一条指令去执行多个数据)
原本是要做四次同样的操作,通过SIMD,也就是将A1 A2 A3 A4放到一起,把B1 B2 B3 B4也放到一起,最后输出的结果C1 C2 C3 C4也放到一起,那么我们只需要执行一次”取指,读取数据,计算,写回数据“就可以了。
CUDA的tensor core非常擅长做4*4矩阵的乘法和加法。
具体来说,原本乘法(scalar operation)要执行16次,但是SIMD可以允许(A0,0 A0,1 A0,2 A0,3) 这一行 同时执行并写回结果。
SIMT Thread线程 在CUDA编程中,线程Tread是一个非常重要的概念,怎么利用GPU里的多个CUDA CORE(多核资源)呢?
用多线程化,用其来对卷积操作、矩阵操作来做优化处理。之后会详细讲解。
OpenMP,pthread:由二者声明了代码块之后,程序声明后的这一部分就可以并行进行。其可以是指令级别的并行化,也可以是代码块级别的并行化。
上边讲的,Multi Core Processor 是同构架构的,也就是Core的类型是相同的,都为CPU Core或者都为CUDA Core等等.
Heterogeneous Multicore Processor 是异构架构,也就是其Core的类型是不同的,CPU Core和GPU Core可以同时存在,还可以有DSP Core Fast Core等等。
我们在做模型部署的时候,不仅要想怎么让这个模型在GPU上变快,我们还要想模型的CPU和GPU之间的通信是什么样子的,我们哪些步骤放在CPU上执行快一点,哪些步骤放在GPU上执行快一点,哪些步骤放在DSP Core上快一点,以及即使有些计算可以用GPU加速,但是是否真的用GPU处理这块计算是最佳解。这就涉及了异步执行的做法,之后也会进行扩展。
本节课 (1.1 并行处理简介) 到此结束。
① 全网独家视频课程
BEV感知、毫米波雷达视觉融合、多传感器标定、多传感器融合、多模态3D目标检测、点云3D目标检测、目标跟踪、Occupancy、cuda与TensorRT模型部署、协同感知、语义分割、自动驾驶仿真、传感器部署、决策规划、轨迹预测等多个方向学习视频(扫码学习)
视频官网:www.zdjszx.com② 国内首个自动驾驶学习社区
近2000人的交流社区,涉及30+自动驾驶技术栈学习路线,想要了解更多自动驾驶感知(2D检测、分割、2D/3D车道线、BEV感知、3D目标检测、Occupancy、多传感器融合、多传感器标定、目标跟踪、光流估计)、自动驾驶定位建图(SLAM、高精地图、局部在线地图)、自动驾驶规划控制/轨迹预测等领域技术方案、AI模型部署落地实战、行业动态、岗位发布,欢迎扫描下方二维码,加入自动驾驶之心知识星球,这是一个真正有干货的地方,与领域大佬交流入门、学习、工作、跳槽上的各类难题,日常分享论文+代码+视频,期待交流!
③【自动驾驶之心】技术交流群
自动驾驶之心是首个自动驾驶开发者社区,聚焦目标检测、语义分割、全景分割、实例分割、关键点检测、车道线、目标跟踪、3D目标检测、BEV感知、多模态感知、Occupancy、多传感器融合、transformer、大模型、点云处理、端到端自动驾驶、SLAM、光流估计、深度估计、轨迹预测、高精地图、NeRF、规划控制、模型部署落地、自动驾驶仿真测试、产品经理、硬件配置、AI求职交流等方向。扫码添加汽车人助理微信邀请入群,备注:学校/公司+方向+昵称(快速入群方式)
④【自动驾驶之心】平台矩阵,欢迎联系我们!