# CPU|GPU程序执行流程

### CUDA和GPU简介

&#x20;CUDA（Compute Unified Device Architecture），是显卡厂商[NVIDIA](https://baike.baidu.com/item/NVIDIA?fromModule=lemma_inlink)推出的运算平台。 CUDA™是一种由NVIDIA推出 的通用[并行计算](https://baike.baidu.com/item/%E5%B9%B6%E8%A1%8C%E8%AE%A1%E7%AE%97/113443?fromModule=lemma_inlink)架构，该架构使[GPU](https://baike.baidu.com/item/GPU?fromModule=lemma_inlink)能够解决复杂的计算问题。 它包含了CUDA[指令集架构（ISA）](https://baike.baidu.com/item/%E6%8C%87%E4%BB%A4%E9%9B%86%E6%9E%B6%E6%9E%84?fromModule=lemma_inlink)以及GPU内部的并行 计算引擎。 开发人员可以使用[C语言](https://baike.baidu.com/item/C%E8%AF%AD%E8%A8%80?fromModule=lemma_inlink)来为CUDA™架构编写程序，所编写出的程序可以在支持CUDA™的处理器上以超高 性能运行。CUDA3.0已经开始支持[C++](https://baike.baidu.com/item/C%2B%2B?fromModule=lemma_inlink)和[FORTRAN](https://baike.baidu.com/item/FORTRAN?fromModule=lemma_inlink)。&#x20;

GPU（Graphic Processing Unit）：图形处理器，显卡的处理核心。电脑显示器上显示的图像，在显示在显示器上之 前，要经过一些列处理，这个过程有个专有的名词叫“渲染"。以前的计算机上没有GPU，渲染就是CPU负责的。渲染是 个什么操作呢，其实就是做了一系列图形的计算，但这些计算往往非常耗时，占用了CPU的一大部分时间。而CPU还要处 理计算机器许多其他任务。因此就专门针对图形处理的这些操作设计了一种处理器，也就是GPU。这样CPU就可以从繁 重的图形计算中解脱出来。&#x20;

NVIDIA公司在1999年发布Geforce 256图形处理芯片时首先提出GPU的概念，随后大量复杂的应 用需求促使整个产业蓬勃发展至今。&#x20;

最早的GPU是专门为了渲染设计的，那么他也就只能做渲染的那些事情。渲染这个过程具体来说就 是几何点、位置和颜色的计算，这些计算在数学上都是用四维向量和变换矩阵的乘法，因此GPU也 就被设计为专门适合做类似运算的专用处理器了。但随着GPU的发展，GPU的功能也越来越多， 比如现在很多GPU还支持了硬件编解码。

全球GPU巨头：NVIDIA(英伟达)，AMD(超威半导体)。

<figure><img src="https://3100114358-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FyhXXzVY3gexs8StunmSt%2Fuploads%2FMx2BacyReTNzUip2Uk5s%2F%E5%9B%BE%E7%89%87.png?alt=media&#x26;token=f0268778-1f08-4e1f-88f9-30224dfa0ad9" alt="" width="563"><figcaption></figcaption></figure>

### GPU与CPU的区别

GPU采用流式并行计算模式，可对每个数据行独立的并行计算。&#x20;

1. CPU基于低延时设计，由运算器（ALU，Arithmetic and Logic Unit 算术逻辑单元）和控制器 （CU，Control Unit），以及若干个寄存器和高速缓冲存储器组成，功能模块较多，擅长逻辑控制，**串行运算。**&#x20;
2. GPU基于大吞吐量设计，拥有更多的ALU用于数据处理，适合对密集数据进行并行处理，擅长大规模并发计算，因此GPU也被应用于AI训练等需要大规模**并发计算**场景。

<figure><img src="https://3100114358-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FyhXXzVY3gexs8StunmSt%2Fuploads%2Fa2UKXLqdrdEBV14gxnHj%2F%E5%9B%BE%E7%89%87.png?alt=media&#x26;token=bd202431-c2b3-44ff-9760-42f0a72a1b80" alt="" width="563"><figcaption></figcaption></figure>

### GPU分类

* 独立GPU：独立GPU一般封装在独立的显卡电路板上，使用专用的显示存储器，独立显卡性能由GPU性能与显存 带宽共同决定。一般来讲，独立GPU的性能更高，但因此系统功耗、发热量较大。&#x20;
* 集成GPU：集成GPU常和CPU共用一个Die，共享系统内存。集成GPU的制作由CPU厂家完成，因此兼容性较强， 并且功耗低、发热量小。但如果显卡运行需要占用大量内存，整个系统运行会受限，此外系统内存的频率通常 比独立显卡的显存低很多，因此一般集成GPU的性能比独立GPU更低。

<figure><img src="https://3100114358-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FyhXXzVY3gexs8StunmSt%2Fuploads%2FpCWYzYr4hoHsoqwmNGSY%2F%E5%9B%BE%E7%89%87.png?alt=media&#x26;token=de0fde6e-9704-4675-a6ea-c8a3f5f5c1da" alt=""><figcaption></figcaption></figure>

### 串行处理与并行处理的区别

<mark style="color:red;">**Sequential processing(串行)**</mark>&#x20;

* 指令/代码块依次执行&#x20;
* 前一条指令执行结束以后才能执行下一条语句&#x20;
* 一般来说，当程序有数据依赖or分支等这些情况下需要串行
* 使用场景: 复杂的逻辑计算(比如:操作系统)

<figure><img src="https://3100114358-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FyhXXzVY3gexs8StunmSt%2Fuploads%2Fbj613XHwicgWOZNW8EGq%2F%E5%9B%BE%E7%89%87.png?alt=media&#x26;token=9b0fe2e9-0aff-4e93-bbb0-2e9d6db574cb" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
data dependency的种类&#x20;

* Flow dependency
* &#x20;Anti dependency&#x20;
* Output dependency&#x20;
* Control dependency
  {% endhint %}

看一个简单的案例：statement 2 依赖 statement 1 ，statement 5 依赖 statement 4 ，statement 3 是一个 循环，跟所有的statement没有任何关系 ，有四个core可以使用，如果是串行需要多少个时钟周期呢？

<figure><img src="https://3100114358-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FyhXXzVY3gexs8StunmSt%2Fuploads%2FPypTkny70oGCBdzNjUOm%2F%E5%9B%BE%E7%89%87.png?alt=media&#x26;token=46d02a6f-9b55-4dcf-9330-c406e19503d6" alt=""><figcaption></figcaption></figure>

一共用时46个时钟周期，改进下，没有任何依赖关系的statement让他们并行执行：

<figure><img src="https://3100114358-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FyhXXzVY3gexs8StunmSt%2Fuploads%2FMKkGHDf6IWKzZplUWuAx%2F%E5%9B%BE%E7%89%87.png?alt=media&#x26;token=82dd2c37-9e55-4323-b3b7-5770ce9d68c3" alt="" width="508"><figcaption></figcaption></figure>

statement3的循环可以分割成多个子代码执行(每个子代码快 5cycles)，进一步可以优化：

<figure><img src="https://3100114358-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FyhXXzVY3gexs8StunmSt%2Fuploads%2Fb41QL1rhSkFRWDO5ffqi%2F%E5%9B%BE%E7%89%87.png?alt=media&#x26;token=37c159d8-c6ec-4528-ad42-780803918bf1" alt="" width="442"><figcaption></figcaption></figure>

statement 5 可以在 statement 4 彻底执行结束前就知道所依赖的结果了

<figure><img src="https://3100114358-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FyhXXzVY3gexs8StunmSt%2Fuploads%2FhvqYUaxJrbs6RpDvHhQF%2F%E5%9B%BE%E7%89%87.png?alt=media&#x26;token=994d79f2-df92-4651-95dd-c0bab47a8511" alt="" width="380"><figcaption></figcaption></figure>

回顾一下，我们在这里都做了哪些事情&#x20;

* 把没有数据依赖的代码分配到各个core各自执行 (schedule, 调度)&#x20;
* 把一个大的loop循环给分割成多个小代码，分配到各个core执行(loop optimization)&#x20;
* 在一个指令彻底执行完以前，如果已经得到了想要得到的数据，可以提前执行下 一个指令(pipeline, 流水线）&#x20;

我们管这一系列的行为，称作parallelization (并行化)。我们得到的可以充分利用多 核多线程的程序叫做parallelized program(并行程序）

<mark style="color:red;">**Parallel processing(并行)**</mark>&#x20;

* 指令/代码块同时执行&#x20;
* 充分利用multi-core(多核)的特性,多个core一起去完成一个或多个任务
* 使用场景:科学计算,图像处理,深度学习等等

<figure><img src="https://3100114358-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FyhXXzVY3gexs8StunmSt%2Fuploads%2F1VZchdpmPsC9RhCKcTAy%2F%E5%9B%BE%E7%89%87.png?alt=media&#x26;token=0bab7f21-2fd2-415c-8877-003bffe773bd" alt=""><figcaption></figcaption></figure>

* Loop parallelization&#x20;

大部分消耗时间长的程序中,要不然就是在I/O上的内存读写消耗时间上长,要不然 就是在loop上。针对loop的并行优化是很重要的一个优化策略 在图像处理/深度学习中很多地方都是用到了循环&#x20;

* 比如说:pre/post process (前处理后处理) ，resize, crop, blur, bgr2rgb, rgb2gray, dbscan, findCounters&#x20;
* 比如说:DNN中的卷积(convolution layer)以及全连接层(Fullyconnected layer)

<mark style="color:red;">**容易混淆的几个概念**</mark>

"并行"与"并发"的区别

* 并行(parallel) -物理意义上同时执行
* 并发(concurrent) -逻辑意义上同时执行

<figure><img src="https://3100114358-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FyhXXzVY3gexs8StunmSt%2Fuploads%2FUcu7QTqRRdW8Ta7LY58z%2F%E5%9B%BE%E7%89%87.png?alt=media&#x26;token=828d1fb1-c74b-49ae-a656-4e2446cd312c" alt="" width="563"><figcaption></figcaption></figure>

“进程”与“线程”的关系

* 线程是进程的子集，一个进程可以有多个线程

"多核"与"加速比"的关系&#x20;

* 双核的加速不一定就是两倍&#x20;
* 8核的加速比有时会差于4核

<figure><img src="https://3100114358-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FyhXXzVY3gexs8StunmSt%2Fuploads%2FUOJPAEoUbvbk41fPljOs%2F%E5%9B%BE%E7%89%87.png?alt=media&#x26;token=13ad08f6-d281-4b48-a3a6-48fa73ee5ae8" alt="" width="563"><figcaption></figcaption></figure>

### CPU与GPU在并行处理的优化方向

* CPU: 目标在于减少memory latency&#x20;
* GPU: 目标在于提高throughput

**memory latency是什么？**

CPU/GPU从memory获取数据所需要的等待时间

<figure><img src="https://3100114358-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FyhXXzVY3gexs8StunmSt%2Fuploads%2FtujuCeWoQaGigIXcDelg%2F%E5%9B%BE%E7%89%87.png?alt=media&#x26;token=1cd11053-37d7-4624-812a-699115f20a3c" alt="" width="481"><figcaption></figcaption></figure>

<figure><img src="https://3100114358-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FyhXXzVY3gexs8StunmSt%2Fuploads%2FGS5kmWU2OLVUDKnnCXeK%2F%E5%9B%BE%E7%89%87.png?alt=media&#x26;token=e1343669-1668-415b-8c51-d66d821c9910" alt=""><figcaption></figcaption></figure>

`cache miss` 这个时候，CPU core由于没有数据，所以 在等待数据的到来。这个状态叫做`stall`，

如果数据不在cache里，那么就需要往下级 memory中寻找数据，然而访问下级memory是很耗时的。

<figure><img src="https://3100114358-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FyhXXzVY3gexs8StunmSt%2Fuploads%2FJFTUoQgyzLieSnDoAdWC%2F%E5%9B%BE%E7%89%87.png?alt=media&#x26;token=0f40f412-d531-469b-afb1-78ad3e6be8d7" alt=""><figcaption></figcaption></figure>

不同memory在latency上的比较

<figure><img src="https://3100114358-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FyhXXzVY3gexs8StunmSt%2Fuploads%2FmNKRHyYjngVkqurir5sH%2F%E5%9B%BE%E7%89%87.png?alt=media&#x26;token=e42687b2-0109-4fa8-b08b-8f9afc11a7c6" alt=""><figcaption></figcaption></figure>

### CPU是如何进行优化的？

* <mark style="color:red;">`Pipeline`</mark>&#x20;

提高throughput的一种优化

<figure><img src="https://3100114358-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FyhXXzVY3gexs8StunmSt%2Fuploads%2FqCKVK5QJtPcQjt8xXbtQ%2F%E5%9B%BE%E7%89%87.png?alt=media&#x26;token=c649f46f-89b8-4c8e-98e6-ef7c31835b76" alt="" width="563"><figcaption></figcaption></figure>

* IF: `Instruction fetch` 把指令从memory中取出来&#x20;
* ID: `Instruction decode` 把取出来的指令给解码成机器可以识别的信号&#x20;
* OF: `Operand fetch` 把数据从memory中取出来放到register中 &#x20;
* EX: `Execution` 使用ALU(负责运算的unit)来进行计算
* WB: `write back` 把计算完的结果写回register

<figure><img src="https://3100114358-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FyhXXzVY3gexs8StunmSt%2Fuploads%2F0iyYadQFOy2GuCT0AQKn%2F%E5%9B%BE%E7%89%87.png?alt=media&#x26;token=533e5f8c-f673-4b2b-8b55-46f66f07066c" alt=""><figcaption></figcaption></figure>

<figure><img src="https://3100114358-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FyhXXzVY3gexs8StunmSt%2Fuploads%2FEAfFkKwFukV7Mfuvk7CP%2F%E5%9B%BE%E7%89%87.png?alt=media&#x26;token=d1823e0b-1f01-4023-8ec3-6d69b056bca8" alt="" width="563"><figcaption></figcaption></figure>

* `cache hierarchy` (多级缓存)：减少`memory latency`的一种优化
  * L1: 逻辑核独占&#x20;
  * L2: 物理核独占&#x20;
  * L3: 所有物理核共享
* `Pre-fetch` :减少memory latency的一种优化

<figure><img src="https://3100114358-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FyhXXzVY3gexs8StunmSt%2Fuploads%2FXDK0rH5WopJELJfN6Sqc%2F%E5%9B%BE%E7%89%87.png?alt=media&#x26;token=f07b92ab-2bc9-4515-9b7f-396eb8944db5" alt=""><figcaption></figcaption></figure>

<figure><img src="https://3100114358-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FyhXXzVY3gexs8StunmSt%2Fuploads%2F6Tk2b5RtKrpXZDpAl1zE%2F%E5%9B%BE%E7%89%87.png?alt=media&#x26;token=2470e266-4909-494f-846b-28e3a59a877e" alt="" width="563"><figcaption></figcaption></figure>

{% hint style="info" %}

* 当程序中出现 branch的话，如何进行pre-fetch?

如果预先知道计算A结束后就执行计算B的话，可以预先(pre)把计算B所需要的指令和数据取出来(fetch)，放入到cache中 (`hiding memory latency`)
{% endhint %}

* `Branch-prediction`(分支预测)

简单来说，就是根据以往的branch得走向，去预测下一次branch的走向，并提前执行了相应的指令,CPU硬件上有专门负责分支预测的Unit

```c
while(i < 100) {
    sum += a[i];
    i ++;
}
```

<figure><img src="https://3100114358-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FyhXXzVY3gexs8StunmSt%2Fuploads%2FkPyb2bfdTntaoBGeM0o9%2F%E5%9B%BE%E7%89%87.png?alt=media&#x26;token=6d82f3cc-fa5d-4c17-81e4-74f81787845c" alt=""><figcaption></figcaption></figure>

因为循环判断条件一直都是true，所以预测下一次 循环也是true，那么先把数据取出来进行 pipeline。如果预测失败，rollback回去就好了。

* `Multi-threading`

充分利用计算资源的一种技术 ，让因为数据依赖或者`cache miss`而`stall`的`core`去做一些其他的事情 ，提高throughput的一种技术。

由于CPU处理的大多数都是一些复杂逻辑的计算，有大量的分支以及难以预测的分支方向，所以增加`core`的数 量，增加线程数而带来的`throughput`的收益往往并不是那么高。

去掉复杂的逻辑计算，去掉分支，把大量的简单运算放在一起的话，是不是就可以最大化的提高`throughput`呢？ 答案是yes，这个就是GPU所做的事情。

### 基础GPU架构

<figure><img src="https://3100114358-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FyhXXzVY3gexs8StunmSt%2Fuploads%2F3GatunA3m9d24OkUwSj6%2F%E5%9B%BE%E7%89%87.png?alt=media&#x26;token=88e5ce44-5627-4d3b-80d6-30a19e7de8a0" alt=""><figcaption></figcaption></figure>

GPU为图形图像专门设计，在矩阵运算，数值计算方面具有独特优势，特别是浮点和并行计算上能 优于CPU的数十数百倍的性能。 GPU的优势在于快，而不是效果好。 比如用美团软件给一张图要加上模糊效果，CPU处理的时候从左到右从上到下进行处理。可以考虑 开多核，但是核数毕竟有限制，比如4核、8核 分块处理。

<figure><img src="https://3100114358-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FyhXXzVY3gexs8StunmSt%2Fuploads%2Fs1MqIyXxihKiTzY1hHpV%2F%E5%9B%BE%E7%89%87.png?alt=media&#x26;token=3d6df743-a141-4b68-972a-db681d16d6ab" alt="" width="563"><figcaption></figcaption></figure>

使用GPU进行处理，因为分块之前没有相互的关联关系，可以通过GPU并行处理，就不单只是4、 8分块了，可以切换更多的块，比如16、64等

### GPU的特点

* `multi-threading`技术&#x20;

大量的core，可以支持大量的线程 ，CPU并行处理的threads数量规模：数十个， GPU并行处理的threads数量规模：数千个

* 每一个`core`负责的运算逻辑十分`simple`

CUDA core: D = A \* B + C&#x20;

Tensor core:&#x20;

* 4x4x4的matrix calculation&#x20;
* D = A \* B + C

<figure><img src="https://3100114358-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FyhXXzVY3gexs8StunmSt%2Fuploads%2FjaOsCYH1LKYEP0VBGw5g%2F1%20HSAoDDRGYzf8Tecj79RP0A.webp?alt=media&#x26;token=2c99d83c-414a-42ac-be8b-cec005fbce90" alt=""><figcaption></figcaption></figure>

<figure><img src="https://3100114358-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FyhXXzVY3gexs8StunmSt%2Fuploads%2FpazObiPaKrupUidgjZoa%2F0%20am1WNF8XjEAA-WVe.webp?alt=media&#x26;token=5ee827ac-2fa7-4e13-b4a5-44fd1408eb14" alt="" width="509"><figcaption></figcaption></figure>

* `SIMT`

类似于SIMD的一种概念 ，将一条指令分给大量的thread去执行，thread间的调度是由warp来负责管理 • GPU体系架构中有一个warp schedular，专门负责管理线程调度的。Warp schedular是GPU体系 架构中特有的概念，CPU中 没有这个。

<figure><img src="https://3100114358-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FyhXXzVY3gexs8StunmSt%2Fuploads%2FTFyf69BlBhdxHWprfMZv%2F%E5%9B%BE%E7%89%87.png?alt=media&#x26;token=f554ff26-c257-4afe-9e9f-b501cf3d2dd9" alt=""><figcaption></figcaption></figure>

* 由于`throughput`非常的高，所以相比与`CPU`，`cache miss`所产生的`latency`对性能的影响比较小&#x20;
* `GPU`主要负责的任务是大规模计算(图像处理、深度学习等等)，所以一旦`fetch`好了数据以后，就会一直连续 处理，并且很少`cache miss`

<figure><img src="https://3100114358-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FyhXXzVY3gexs8StunmSt%2Fuploads%2FpuKHrHGy4efskDnhQ0lL%2F%E5%9B%BE%E7%89%87.png?alt=media&#x26;token=138d1512-46f9-4783-8f97-1696effad841" alt="" width="563"><figcaption></figcaption></figure>

### summary

* `CPU`:&#x20;
  * 适合复杂逻辑的运算&#x20;
  * 优化方向在于减少`memory latency`&#x20;
  * 相关的技术有,`cache hierarchy`, `pre-fetch`, `branch-prediction`,`multi-threading`&#x20;
  * 不同于GPU,CPU硬件上有复杂的分支预测器去实现`branch-prediction`&#x20;
  * 由于CPU经常处理复杂的逻辑,过大的增大`core`的数量并不能很好的提高`throughput`&#x20;
* `GPU`:&#x20;
  * 适合简单单一的大规模运算。比如说科学计算,图像处理,深度学习等等&#x20;
  * 优化方向在于提高`throughput`&#x20;
  * 相关的技术有,`multi-threading`,`warp schedular`&#x20;
  * 不同于CPU,GPU硬件上有复杂的`warp schedular`去实现多线程的`multi-threading`&#x20;
  * 由于GPU经常处理大规模运算,所以在throughput很高的情况下,GPU内部的`memory latency`上带来的性 能损失不是那么明显&#x20;
  * 然而CPU和GPU间通信时所产生的`memory latency`需要重视

### reference

* <https://www.zhihu.com/question/35063258>
* <https://lwn.net/Articles/252125/>
* <https://medium.com/codex/understanding-the-architecture-of-a-gpu-d5d2d2e8978b>
* <https://arxiv.org/pdf/1603.07285.pdf>
* <https://pdf.dfcfw.com/pdf/H3_AP202111101528199831_1.pdf?1636553716000.pdf>
