Skip to content

Commit

Permalink
refine 1,7,8 (#212)
Browse files Browse the repository at this point in the history
* add a bit for chip

* fix a bit
  • Loading branch information
YanjieGao authored Jul 17, 2022
1 parent cd31136 commit 0c1b9cd
Show file tree
Hide file tree
Showing 16 changed files with 92 additions and 86 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ if __name__ == '__main__':
例如,图中所示的维度为,输入张量形状(Tensor Shape)为 $3 \times 32 \times 32$(3 代表通道,32 代表张量高度,32 代表张量宽度),经过 $2 \times 3 \times 5 \times 5$ 的卷积(2 代表输出通道数,3 代表输入通道数,5 代表卷积核高度,5 代表卷积核宽度)后,输出张量形状(Tensor Shape)为$2 \times 28 \times 28$(2 代表通道,28 代表高,28 代表宽)。

<center><img src="./img/4/conv3d.png" /></center>
<center>图 1.5.3 Conv2D 计算过程实例 (<a href="https://github.com/microsoft/ai-edu">图片引用</a>)</center>
<center>图 1.5.3 Conv2D 计算过程实例 (<a href="https://github.com/microsoft/ai-edu">图片引用 ai-edu</a>)</center>

我们以卷积算子为例,图中所示的卷积的计算可以表达为多层嵌套循环,我们以下面代码为例进行分析。
```
Expand Down Expand Up @@ -162,7 +162,7 @@ for n in range(batch_size):

<center> <img src="./img/4/1-4-4-imtocol.png" /></center>

图 1.4.4 卷积通过 im2col 转换为通用矩阵乘(图中我们使用一个卷积核和一个输入图片通道为例简要说明)(<a href="https://arxiv.org/pdf/2005.13076.pdf">图片引用[<sup>[3]</sup>](#im2col)</a>)
图 1.4.4 卷积通过 im2col 转换为通用矩阵乘(图中我们使用一个卷积核和一个输入图片通道为例简要说明)(<a href="https://arxiv.org/pdf/2005.13076.pdf">图片引用 PHAST[<sup>[3]</sup>](#im2col)</a>)

- 利用片上内存:其中参与计算的输入,权重和输出张量能否完全放入 GPU 缓存(L1,L2)?如果不能放入则需要通过循环块(Tile)编译优化进行切片,这些内容将在第5章着重介绍。
- 局部性:循环执行的主要计算语句是否有局部性可以利用?空间局部性(缓存线内相邻的空间是否会被连续访问)以及时间局部性(同一块内存多久后还会被继续访问),这样我们可以通过预估后,尽可能的通过编译调度循环执行,这些内容将在第 5 章着重介绍。在 1.5 中我们也将打印矩阵乘的局部性,读者可以参考理解。
Expand Down Expand Up @@ -193,7 +193,7 @@ for n in range(batch_size):
我们通过 LeNet 实现实例,对比 cuDNN + CUDA 这层抽象还不足以让算法工程师非常高效的设计模型和书写算法。如下两个实例所示,同样实现 LeNet,使用高层框架只需要 9 行,而通过 cuDNN 需要上千行代码,而且还需要精心的管理内存分配释放,拼接模型计算图,效率十分低下。

***(1) 通过cuDNN + CUDA API编程实现LeNet,需要~1000行实现模型结构和内存管理等逻辑***
[参考文档](https://github.com/tbennun/cudnn-training/blob/master/lenet.cu)
[参考实例 cudnn-training](https://github.com/tbennun/cudnn-training/blob/master/lenet.cu)
```C++
// 内存分配,如果用深度学习框架此步骤会省略
...
Expand Down Expand Up @@ -229,7 +229,7 @@ cudaFree(d_conv1);
...
```
***(2) 通过 Keras 书写 LeNet (TensorFlow Backend)[<sup>[5]</sup>](#keraslenet),只需要 9 行构建模型结构,算上训练逻辑只需要几十行代码***
[参考文档](https://github.com/TaavishThaman/LeNet-5-with-Keras/blob/master/lenet_5.py)
[参考文档 LeNet-5-with-Keras](https://github.com/TaavishThaman/LeNet-5-with-Keras/blob/master/lenet_5.py)
```python
model = keras.Sequential()
model.add(layers.Conv2D(filters=6, kernel_size=(3, 3), activation='relu', input_shape=(32,32,1)))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ ret; // 返回
- 分解(Dissaggregation):数据中希望计算,内存,网络分解(Dissaggregation)提供更加灵活多样适合多样数据中心负载需求的资源配置,例如,以分解内存(Dissaggregation Memory)管理为代表的工作([MIND SOSP '21](https://dl.acm.org/doi/10.1145/3477132.3483561) 等)需要重新设计和卸载页表等基本功能。
- 抽象与统一管理(Unified Management):对计算或存储异构硬件,常常抽象在统一的空间内进行管理,最终达到对用户透明。例如,NVIDIA 的[统一内存(Unified Memory)](https://developer.nvidia.com/blog/unified-memory-cuda-beginners/)就类似传统的虚拟内存(Virtual Memory)的思想统一管理 GPU 显存和主存。Intel 的[oneAPI](https://www.oneapi.io/)统一编程模型,管理 CPU 和GPU算力。也有在框架层进行抽象与统一管理的工作,例如,[BytePS OSDI '20](https://www.usenix.org/conference/osdi20/presentation/jiang) 协同利用 CPU 和 GPU 训练模型。
<center><img src="img/5/5-1-9-Heterogeneoushardware.png" /></center>
<center>图1-5-2. 深度学习程序异构计算 (<a href="">图片引用</a>)</center>
<center>图1-5-2. 深度学习程序异构计算</center>

- ***[内存墙](https://en.wikipedia.org/wiki/Random-access_memory#Memory_wall)约束***
- [“内存墙”](https://en.wikipedia.org/wiki/Random-access_memory#Memory_wall)是芯片与芯片外的内存之间越来越大的速度差距。造成这种差距的一个重要原因是超出芯片边界的有限通信带宽,也称为带宽墙。例如,从 1986 年到 2000 年,CPU 速度以每年 55% 的速度提高,而内存速度仅提高了 10%。鉴于这些趋势,预计内存延迟将成为计算机性能的压倒性瓶颈。虽然增速比例不同,但是这种通常计算发展更快的现状其实我们在 GPU 的发展过程中也可以观察到,例如表 1.5.1 所示,NVIDIA H100 相比 A100 在 FP32 Vector 上是 48TFLOPS 相比 19.5TFLOPS 的近 2.46 倍提升,但是内存带宽只是 2TB/s (PCIe 版 GPU)比 1,935GB/s (PCIe 版 GPU)的 1.06 倍提升,访存落后于计算的提升速度,如果我们考虑新型号 GPU 中计算单元进行稀疏,低精度量化加速,这个差距可能会变得更大。可以认为在人工智能领域内存墙这种情况还存在。一般应对内存墙有以下思路进行缓解:(1)利用缓存减少数据访存搬运,同时我们在后面的编译优化等章节会看到很多策略是尽可能利用片上共享内存或者GPU显存,尽可能的减少数据搬运。(2)[关联处理(Associative Processing)的思想和关联处理器(Associative Processors)](https://ieeexplore.ieee.org/document/330035),将计算直接卸载到数据侧,例如,[IMCA TVLSI '21 加速器设计](https://ieeexplore.ieee.org/document/9324735)。除了硬件支持,软件层也可以借鉴其思想。在传统大数据系统中,Spark 等尽可能搬运 UDF,将计算放在数据块所在节点处理,减少搬运数据,以及 Big Table 和 HBase 的[协处理器(Coprocessor)](https://blogs.apache.org/hbase/entry/coprocessor_introduction)设计都是关联处理思想的延伸。(3)减少数据本身,例如,利用稀疏性与低精度量化,减少待计算需要传递的的数据量。(4)计算屏蔽数据搬运开销,例如,GPU 通过更小开销的硬件线程切换设计,让需要访存的线程让出计算核,进而提升整体一批线程的计算吞吐,减少访问的开销。
Expand Down Expand Up @@ -198,9 +198,7 @@ ret; // 返回

<center><img src="img/5/5-1-3-MemoryHierarchy.png" /></center>

图 1.5.3 内存存储层级 (<a href="https://diveintosystems.org/book/C11-MemHierarchy/mem_hierarchy.html">图片引用[<sup>[]</sup>](#dive)</a>)

图 1.5.3 内存层次结构(Memory Hierarchy)[图片引用](https://diveintosystems.org/book/C11-MemHierarchy/mem_hierarchy.html)[<sup>[10]</sup>](#dive)
图 1.5.3 内存层次结构(Memory Hierarchy)[图片引用 Dive Into Systems](https://diveintosystems.org/book/C11-MemHierarchy/mem_hierarchy.html)[<sup>[10]</sup>](#dive)
在图中我们看到传统的内存层次结构问题总结中没有覆盖 GPU 和 AI 加速器内存,缓存和寄存器,但是其相对规律和上图是一致的,我们可以通过 PCIe 总线将 AI 加速器与当前已有体系结构互联,让计算机利用 AI 加速器加速模型的训练与推理。

<center><img src="img/5/5-1-13-storagevslogistics.png" /></center>
Expand All @@ -211,12 +209,12 @@ ret; // 返回
图 1.5.5 中我们可以看到 GPU 显存和主存之间通过 PCIe 总线传输数据,一般是初始时的模型权重,以及每批次的输入数据张量。

<center><img src="img/5/5-1-2-host_device.png" /></center>
<center>图 1.5.5. 主机和 GPU 之间的数据传输,GPU 显存和主存关系 (<a href="https://www.telesens.co/2019/02/16/efficient-data-transfer-from-paged-memory-to-gpu-using-multi-threading/">图片引用</a>)</center>
<center>图 1.5.5. 主机和 GPU 之间的数据传输,GPU 显存和主存关系 (<a href="https://www.telesens.co/2019/02/16/efficient-data-transfer-from-paged-memory-to-gpu-using-multi-threading/">图片引用 ankur6ue 博客</a>)</center>

图 1.5.6 中我们可以看到 GPU 显存和缓存之间的数据流动。

<center><img src="img/5/5-1-1-gpumem.png" ch="500" /></center>
<center>图 1.5.6 GPU 缓存与 GPU 内存数据传输,GPU 显存与缓存关系(<a href="https://docs.nvidia.com/deeplearning/performance/dl-performance-gpu-background/index.html">图片引用</a>)</center>
<center>图 1.5.6 GPU 缓存与 GPU 内存数据传输,GPU 显存与缓存关系(<a href="https://docs.nvidia.com/deeplearning/performance/dl-performance-gpu-background/index.html">图片引用 NVIDIA 文档</a>)</center>

接下来我们以一个算子也就是卷积转换为矩阵乘计算后为例,看负载的局部性,这个情况下假设数据已经完成图 1.5.5 中读取,数据在 GPU 显存,所以只需要关注图 1.5.6 中的 GPU 显存与缓存之间的关系,分析矩阵乘的访存的局部性。

Expand Down Expand Up @@ -300,7 +298,7 @@ printf("%d \n", &C[m][n]);
由于矩阵运算含有较少的的控制流,且计算之间依赖少,适合并行计算。大量的矩阵乘等计算让硬件可以通过单指令多数据流(SIMD)进行指令流水线精简设计,并将更多的片上资源用于计算,如图 1.5.9 所示。或者在更高的层次上通过多卡或分布式计算方式进行加速。这种特性的应用我们将在第 5 章中进行总结。同时我们也看到,由于矩阵计算早在几十年前在科学计算与高性能计算(HPC)领域有过大量的成熟研究,在深度学习系统领域也有很多工作会借鉴并优化传统科学计算与高性能计算领域的系统设计与开源组件。

<center><img src="img/5/5-1-4-sisd-simd.png" /></center>
<center>图 1.5.9 SISD 对比 SIMD (<a href="">图片引用</a>)</center>
<center>图 1.5.9 SISD 对比 SIMD</center>

如下图所示,我们通过 A,B 进行通用矩阵乘计算产生矩阵 C(全连接,卷积和平均池化等通常可以转换为通用矩阵乘)。但是我们观察到,下面都是标量的操作,如果每个线程都启动进行标量运算,相当于每个线程都是一个指令流和数据流,但是每个线程实际执行的是相同指令,造成了指令访存和流水线的浪费。

Expand Down Expand Up @@ -348,7 +346,7 @@ else
束曲发散会导致并行效率的巨大损失,在最坏的情况下,如果出现以下情况,性能实际上会损失 32 倍(束一般设置为 32 线程),是由于一个线程需要昂贵的分支,而其余的什么也不做。一般开发人员程序端可以选择使用以下优化:如果边界条件便宜,则遍历所有节点并根据边界条件的需要进行分支。如果边界条件很昂贵,推荐拆分为两个 CUDA 内核(Kernel)。硬件层,NVIDIA Volta 系列的[独立线程调度(Independent Thread Scheduling)](https://developer.nvidia.com/blog/using-cuda-warp-level-primitives/)支持从不同分支交叉执行语句。这使得能够执行细粒度并行算法,束(warp)中的线程可以同步和通信。如下图 1.5.10 所示,Volta 独立线程调度支持从不同分支交叉执行语句。这使得能够执行细粒度并行算法,其中 Warp 中的线程可以同步和通信。虽然软件和硬件层尽可能的规避束发散(Warp Divergence)影响,我们还是看到如图所示其造成部分计算单元在一定时间内的浪费问题,所以问题的本质还是特定的硬件和编程模型适合特定的负载,对负载和硬件执行特点的理解是开掘硬件的计算能力达到软件层优化上限的关键。所以读者可以进一步思考 GPU 适合什么样的负载,以及局限性?

<center><img src="img/5/5-1-11-interlevedexec.png" ch="500" /></center>
<center>图 1.5.10 线程交替执行实例 (<a href="https://developer.nvidia.com/blog/using-cuda-warp-level-primitives/">图片引用</a>)</center>
<center>图 1.5.10 线程交替执行实例 (<a href="https://developer.nvidia.com/blog/using-cuda-warp-level-primitives/">图片引用 NVIDIA 文档</a>)</center>

当前问题的并行优化读者可以通过 1.5.5 中介绍的 ***阿姆达尔定律的约束*** 进行思考和分析并行后的加速上限是多少?

Expand Down Expand Up @@ -398,13 +396,13 @@ else
- 强化学习训练模式并行:多个智能体(Agent)可以并行执行,模型本身可以并行执行。
- 推理中的并行:内核内与内核间都有较大的并行执行机会。

但是并行计算的天花板到底在哪里?如何提前评估并行加速的上限?[阿姆达尔定律](https://en.wikipedia.org/wiki/Amdahl%27s_law)[<sup>[12]</sup>](#amdahl)就是为回答以上问题而产生。阿姆达尔定律为:$S_{latency}(s) = \frac{1}{(1-p) + \frac{p}{s}}$:其中$S_{latency}$ 是整个任务执行的理论加速;$s$ 是从改进的系统资源中受益的部分任务的加速;$p$ 是受益于改进资源的部分最初占用的执行时间的比例。我们可以看到并行加速受限于串行部分,之后的深度学习中,并行过程中的阶段与阶段间的同步点,聚合运算等都是需要一定串行并无法并行的部分。
但是并行计算的天花板到底在哪里?如何提前评估并行加速的上限?[阿姆达尔定律(引用 Wikipedia)](https://en.wikipedia.org/wiki/Amdahl%27s_law)[<sup>[12]</sup>](#amdahl)就是为回答以上问题而产生。阿姆达尔定律为:$S_{latency}(s) = \frac{1}{(1-p) + \frac{p}{s}}$:其中$S_{latency}$ 是整个任务执行的理论加速;$s$ 是从改进的系统资源中受益的部分任务的加速;$p$ 是受益于改进资源的部分最初占用的执行时间的比例。我们可以看到并行加速受限于串行部分,之后的深度学习中,并行过程中的阶段与阶段间的同步点,聚合运算等都是需要一定串行并无法并行的部分。

如图 1.5.12 所示,根据阿姆达尔定律,作为执行程序的处理器数量的函数(也可以推广到分布式或者多设备场景进行理论分析),程序执行延迟的理论加速(y 轴)。程序的加速受到程序的串行部分的限制。例如,如果 95% 的程序可以并行化(Parallel Portion = 95%),那么理论上使用并行计算的最大加速将是 20 倍(图中绿色曲线所示)。

<center><img src="img/5/5-1-6-AmdahlsLaw.png" /></center>

图 1.5.12 阿姆达尔定律 (<a href="https://en.wikipedia.org/wiki/Amdahl%27s_law">图片引用[<sup>[12]</sup>](#amdahl)</a>)
图 1.5.12 阿姆达尔定律 (<a href="https://en.wikipedia.org/wiki/Amdahl%27s_law">图片引用 Wikipedia[<sup>[12]</sup>](#amdahl)</a>)

通过阿姆达尔定律我们可以看到如果某段程序只有百分之 50% 才能优化最终不管怎样增加核最多达到 2 倍提升上限,继续增加只会让 ***性价比(Cost Performance)*** 降低。

Expand All @@ -420,7 +418,7 @@ else
- 存储:分布式文件系统(例如,[HDFS](https://hadoop.apache.org/docs/r1.2.1/hdfs_design.html)[Alluxio](https://www.alluxio.io/)[Azure Blob](https://azure.microsoft.com/en-us/services/storage/blobs/) 等)中的副本机制。

<center><img src="img/5/5-1-5-duplication.png" /></center>
<center>图 1.5.13 副本机制 (<a href="">图片引用</a>)</center>
<center>图 1.5.13 副本机制 </center>

由于冗余(Redundancy)副本会产生一致性(Consistency)的问题,工程师可以参考经典的分布式系统保持副本一致性的共识(Consensus)协议(例如,[Paxos](https://en.wikipedia.org/wiki/Paxos_(computer_science))[Raft](https://en.wikipedia.org/wiki/Raft_(algorithm)) 等)保证新设计的副本系统数据一致和能够做领袖选举(Leader Election)从故障中恢复。

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,12 +131,12 @@
如图 7.1.4 所示,一般本地部署初始一次性投入较大,在初始的几年成本高于云计算按需付费的方式,云计算一般在一定年份之后,成本会逐渐超过本地部署。所以云适合公司规模较小和发展初期,等业务稳定和体量大后,机构可以选择自建本地集群或者采用混合云方式,降本增效。

<center><img src="./img/1/7-1-4-publicvspremisecloud.png" /></center>
<center>图 7.1.4 本地与云部署成本趋势 (<a href="https://www.scirp.org/journal/paperinformation.aspx?paperid=87661">图片来源</a>) </center>
<center>图 7.1.4 本地与云部署成本趋势 (<a href="https://www.scirp.org/journal/paperinformation.aspx?paperid=87661">图片引用 Cameron Fisher 文章</a>) </center>

对是否将基础架构部署于云上的讨论,其实在 2009 年 UCB RAD Lab 的 Armando Fox 教授曾有一段对比“[Above the Clouds:
A Berkeley View of Cloud Computing](https://www.cs.purdue.edu/homes/bb/BerkeleyCloud.pdf)[<sup>[9]</sup>](#aboveclouds)。在今天看来也可以用来作为衡量面向深度学习的异构资源管理系统更为底层的部署模式的选型条件。

<center>图 7-1-1 公有云与私有云对比 (<a href="https://www.cs.purdue.edu/homes/bb/BerkeleyCloud.pdf">来源</a>) </center>
<center>图 7-1-1 公有云与私有云对比 (<a href="https://www.cs.purdue.edu/homes/bb/BerkeleyCloud.pdf">表格引用 Armando Fox 演讲 '09</a>) </center>

|好处|公有云|私有云|
|---|---|---|
Expand Down
Loading

0 comments on commit 0c1b9cd

Please sign in to comment.