Skip to content

Commit

Permalink
update DeepUM-ASPLOS23
Browse files Browse the repository at this point in the history
  • Loading branch information
LujhCoconut committed Mar 11, 2024
1 parent a4171ac commit 6cfc82a
Show file tree
Hide file tree
Showing 7 changed files with 105 additions and 5 deletions.
9 changes: 6 additions & 3 deletions ASPLOS/DeepUM.html

Large diffs are not rendered by default.

96 changes: 95 additions & 1 deletion ASPLOS/DeepUM.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
* 提出了一种专门预取DNN中预取页面的相关预取技术。这两个相关表记录了DNN训练阶段的内核执行历史和页面访问模式。
* DeepUM可以以更大的批处理大小运行模型,而以前的方法无法运行。


> 个人评价:利用相关性用两种相关表对GPU执行内核和预取UM块进行“链式反应”式预取
------

Expand Down Expand Up @@ -156,3 +156,97 @@ DeepUM驱动程序。DeepUM驱动程序处理GPU页面故障,并预取页面
------

## **GPU页面的关联预取**



### **Pair-based correlation prefetching**

Pair-based相关预取在相关表中记录过去的丢失地址序列.。当cache line miss时,它查找相关表并预取与未命中地址相关的所有地址。图5显示了cache line的pair-based相关预取的示例。

![](pair-based.png)

> Correlation table中的每一****都是N路组相联,以减少映射到同一行的UM块之间的地址冲突。本例为便于说明,假设了1路组相联。
对于每一个Set都有NumLevels个后继Miss addresses层级。对于每一个层级,都有NumSuccs个以MRU(最近最常使用)顺序从左到右排列的表项。Last和SecondLast是两个指针,分别指向当前和上一次miss的地址。

图(a)为例,a表项中第一层级包含b,这是因为b miss发生在a miss之后。而在第二层包含c,因为c miss在b miss之后。如图(b)所示,当a再次miss时,Last指针指向a表项,SecondLast指针指向c表项。同时,b表项和c表项同步更新。a miss在c miss之后,因此b表项的第二层包含a。同理,c表项的第一层包含a。

此时由于重新访问a,并且a表项有后继表项记录(b和c),因此b,c将被预取。





### **DeepUM中的相关预取**

DeepUM中的相关性预取旨在通过预取CUDA内核预期要访问的页面来减少page fault。与原始的相关预取不同,DeepUM使用了两种类型的相关表:*Execution ID correlation table**UM block*。这两种类型的表都有一个级别(𝑁𝑢𝑚𝐿𝑒𝑣𝑒𝑙𝑠= 1),因为预取线程确实有链接。此外,由于DNN工作负载的特性,没有理由维护多个级别。请注意,DeepUM的相关性预取在UM块级别工作,而不是在高速缓存行或页面级别工作。



#### Execution ID correlation table

执行ID相关表(简称为执行表)记录了CUDA内核的执行ID的执行历史记录,并且只有一个表存在。执行id来自于DeepUM运行时。

<img src="execuation ID tablepng.png" style="zoom:67%;" />

执行表中的每个条目都包含相关的执行id集。每个集合由四个执行id组成。前三个id表示在最后一个内核之前执行的内核(更新表时当前执行内核)。因此,最后一个ID表示在发生预取时要执行的下一个内核。例如,执行ID 0的条目的记录为(7、9、92、75)。执行ID 0的条目中的此记录意味着执行ID为7、9和92的内核已被执行,而执行ID为0的内核当前正在执行。它预测接下来要执行的内核的执行ID为75。

每个条目所包含的记录的数量是可变的,也就是说,一个内核的后继内核的数量是可变的。因此,每个条目都可以保存后继内核的执行id的所有历史记录。DeepUM选择这个方案来尽可能准确地预测下一个要执行的内核。尽管预测下一个要访问的UM块的不正确结果并不是那么昂贵,但预测下一个要执行的内核的不准确结果也是昂贵的。



#### **UM block correlation table**

NVIDIA驱动程序通过将页面分组为多个UM块来管理CUDA UM空间中的页面。最多有512个相邻的页面被分组到一个UM块中。UM块相关表(简称块表)在UM块级别记录历史记录,而不是记录故障页面地址的历史记录。选择这种粒度有两个原因。一是对于大规模的dnn,有太多的页面地址需要管理。另一个原因是,DeepUM 的页面管理粒度与英伟达驱动程序相同,因此效率更高。请注意,NVIDIA驱动程序以UM块的粒度来管理页面。

<img src="UM Block.png" style="zoom:67%;" />

每个执行ID都有一个块表,并在相应的CUDA内核中记录UM块访问的历史记录。它类似于在原始相关性预取中使用的表。但是,块表包含指向预取的最后一个UM块的结束UM块(the last UM block prefetched)的地址,以及在相应的内核执行中指向第一个出现故障的UM块的开始UM块。这两个指针用于实现链接。开始UM块和结束UM块都在当前执行的内核的转换期间被捕获。End UM块是页面所在的UM块,在执行ID转换之前最后出现故障。启动UM块是在执行ID转换后发生的第一个故障页面所在的UM块。

考虑图7中的块表,假设DeepUM正在为执行ID为0的内核预取UM块b。还假设执行ID为1的内核将在执行ID为0的内核之后立即执行。b的后继UM块是e和q。由于块q与执行ID为0的结束UM块相同,因此预取线程停止对执行ID为0的内核的预取。然后开始预取块k(用于执行ID1的块表的开始UM块)

这个过程被称为链接(chaining)。它是在页面故障发生后不断计算UM块地址的过程。当一个新的页面故障中断信号出现,或者预取线程无法预测下一个要执行的内核时,链接将结束。当预取线程进入下一个𝑁内核的所有预取命令时,链接会暂停。预取线程在当前执行的内核完成后重新开始

> 整个预取过程就像链式反应一样,当前执行内核会一直预取直到遇到end指针指向的UM块,中间的后继链式查找过程可以通过块的后继链在一起;除了链式反应进行到end指针指向的UM块这种情况,还有可能出现后继块根本指不到end指针指向的UM块。简单举例,假设a的后继是b,c而b的后继是c,a,c的后继是a,b,end指针指向的是e。那么在这种情况下,a,b,c都取完,链式反应就进行不下去了。这两种情况都会使得当前执行内核预取任务终止进而转而执行下一执行内核进行执行。




### 页面驱逐和预驱逐

当驱动程序无法为迁移出现故障的页面分配GPU内存空间时,就会发生页面驱逐。图8显示了发生页面驱逐的场景。当没有GPU内存空间来处理故障页面时,就会发生页面驱逐。页面驱逐需要大量的时间,这意味着**页面驱逐逻辑位于页面故障处理的关键路径**上。因此,当GPU上没有更多的空间时,故障处理时间就会增加。

<img src="page_pre_evict.png" style="zoom:67%;" />

为了最小化故障处理时间,DeepUM驱动程序**会在没有空闲的GPU内存空间时预测页面**。Kim等人[32]也提出了类似的想法。主要的区别是,DeepUM**使用了不同的策略来选择受害者页面**。Kim等人使用的策略与在NVIDIA驱动程序中实现的策略相同。它会驱逐最近很少迁移到GPU的页面。DeepUM驱动程序会驱逐满足以下两个条件的页面:

> Hyojong Kim, Jaewoong Sim, Prasun Gera, Ramyad Hadidi, and Hyesoon Kim. *Batch-Aware Unified Memory Management in GPUs for Irregular Workloads*. Association for Computing Machinery, New York, NY, USA, 1357–1370. https: //doi.org/10.1145/3373376.3378529
* 最近最少迁移。

* 预计不会被当前正在执行的内核和预计要执行的下𝑁个内核所访问。

由于NVIDIA驱动程序跟踪GPU侧的空闲空间,因此DeepUM从NVIDIA驱动程序中获取可用的空间信息。它从执行ID相关表中获取下一个正在执行的内核信息。





#### 使非活动的 PyTorch 块的 UM 块无效

* Pytorch的实现
PyTorch 对 CPU 和 GPU 有不同的内存分配器。PyTorch 的 GPU 内存分配器管理设备内存池,以最小化内存分配/释放时间并减少内存碎片。GPU 内存分配器管理二种类型的内存池:大型和小型。

在 PyTorch 中,一个内存对象被称为块(block)。在这篇论文中,我们称它为 PT 块,以便与 UM 块区分。大型池由大于 1MB 的 PT 块组成,小型池由小于或等于 1MB 的 PT 块组成。

当一个内存分配请求进来,且请求的大小大于 1MB,内存分配器从大型池中找到一个 PT 块。否则,它从小型池中找到一个 PT 块。当池中的多个 PT 块与请求的大小匹配时,分配器返回最小的可用 PT 块。此外,当 PT 块的大小远大于请求的大小时,PT 块会被分割。选择上GPU运行的 PT 块从内存池中移除,并标记为活动。然而,当内存池中没有可用的 PT 块时,GPU 内存分配器通过向 CUDA 运行时请求设备内存空间来分配新的 PT 块。

在 DNN 模型使用 PT 块并将其返回给 GPU 内存分配器后,分配器将 PT 块插入到适当的内存池并标记为非活动。只有当池中没有剩余的可用内存空间时,非活动的 PT 块(即内存池中的 PT 块)才会被释放以产生新的内存空间。

Pytorch实现的问题
问题是当我们使用 PyTorch 内存分配器和 UM 时。当 GPU 内存中的非活动 PT 块被驱逐到 CPU 内存时,会有不必要的大量数据流量。此外,它们占用了 CPU 内存空间。当非活动的 PT 块被标记为活动并再次被 DNN 模型使用时,问题会变得更糟。因为 PT 块中的页面已经被驱逐到 CPU 内存,所以

本文的解决方法
为了解决这个问题,我们在 PyTorch 内存分配器中添加了几行代码,当 PT 块被标记为非活动时通知 DeepUM 驱动程序。如果一个受害页面属于非活动的 PT 块,DeepUM 驱动程序简单地使 GPU 内存中对应的 UM 块无效。

Binary file added ASPLOS/UM Block.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added ASPLOS/execuation ID tablepng.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added ASPLOS/page_pre_evict.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added ASPLOS/pair-based.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 6cfc82a

Please sign in to comment.