nvshare
:无内存大小限制的实用GPU共享机制nvshare
是一种GPU共享机制,允许多个进程(或在Kubernetes上运行的容器)安全地同时在同一物理GPU上运行,每个进程都能使用全部GPU内存。
您可以在 https://www.youtube.com/watch?v=9n-5sc5AICY 观看简短的解释和演示。
为实现这一点,它透明地启用GPU页面故障,使用系统RAM作为交换空间。为避免抖动,它使用nvshare-scheduler
来管理GPU,并在给定的时间量子(TQ)内(默认持续30秒)为单个进程提供独占的GPU访问权限。
此功能仅依赖于NVIDIA内核驱动程序提供的统一内存API。NVIDIA内核驱动程序的更新不太可能影响此项目的可行性,因为这需要禁用统一内存。
在Kubernetes上处理GPU的事实上的标准方式(Nvidia的设备插件)是以1:1的方式将GPU分配给容器。这对于仅在执行过程中偶尔使用GPU的应用程序(如长期运行的交互式开发作业,例如Jupyter笔记本)来说特别低效。
我写了一篇Medium文章,讨论了Kubernetes上GPU共享的挑战,值得一读。
cudaMalloc()
时,CUDA应用程序的内存分配总和必须小于物理GPU内存大小(Σ(mem_allocs) <= GPU_mem_size
)。cudaMalloc()
调用为cudaMallocManaged()
,即透明地强制使用CUDA的统一内存API,这不会影响正确性,只会导致约1%的性能下降。cudaMalloc()
编写的应用程序,限制(1)不再适用。Σ(mem_allocs) > GPU_mem_size
)时,我们必须注意避免在共同定位应用程序的工作集(即它们正在主动使用的数据)不适合GPU内存时(Σ(wss) > GPU_mem_size
)发生抖动。我们使用nvshare-scheduler
来序列化GPU上的工作以避免抖动。如果我们不序列化工作,NVIDIA黑盒调度器在共同定位应用程序之间频繁(每几毫秒)的上下文切换将导致抖动。Σ(wss) <= GPU_mem_size
,我们可以禁用nvshare-scheduler
的反抖动模式。nvshare
依赖于Pascal微架构中引入的统一内存动态页面故障处理机制。
它支持任何Pascal(2016)或更新的Nvidia GPU。
它仅在Linux系统上进行过测试。
<a name="overview"/>nvshare
组件nvshare-scheduler
,负责管理单个Nvidia GPU。它在想要在GPU上提交工作的共同定位客户端之间调度GPU"锁"。它以先来先服务的方式为客户端分配GPU的独占访问权限,每次持续TQ秒。libnvshare.so
,我们通过LD_PRELOAD
注入到CUDA应用程序中,它:
nvshare
的客户端,与nvshare-scheduler
实例通信,以在应用程序每次想要在GPU上进行计算时获得对GPU的独占访问权限。nvsharectl
,一个命令行工具,用于配置nvshare-scheduler
实例的状态。nvshare-scheduler
的一些细节重要提示:
nvshare
目前仅支持每个节点一个GPU,因为nvshare-scheduler
硬编码使用ID为0的Nvidia GPU。nvshare-scheduler
的任务是防止内存颠簸。它以先来先服务的方式处理应用程序的请求,每次将整个GPU及其物理内存的独占使用权分配给单个应用程序。每个应用程序最多使用GPU TQ秒。如果应用程序处于空闲状态,它会提前释放GPU。当它稍后想要在GPU上进行计算时,会再次向调度器请求GPU访问权限。当调度器授予它GPU访问权限时,应用程序会通过页面错误逐步将数据获取到GPU。
如果共存应用程序的GPU内存使用总和适合可用的GPU内存,它们可以无缝地并行运行。
然而,当总内存使用超过GPU总内存时,nvshare-scheduler
必须将不同进程的GPU工作序列化,以避免内存颠簸。
nvshare-scheduler的反颠簸模式默认是启用的。你可以使用nvsharectl
进行配置。目前我们没有自动检测颠簸的方法,因此必须手动开关调度器。
nvshare
允许每个共存进程使用整个物理GPU内存。默认情况下,它不允许单个进程分配超过GPU容量的内存,因为这可能导致进程内部颠簸,无论同一GPU上是否存在其他进程。
如果你收到CUDA_ERROR_OUT_OF_MEMORY
错误,意味着你的应用程序试图分配超过GPU总容量的内存。
你可以设置NVSHARE_ENABLE_SINGLE_OVERSUB=1
环境变量,允许单个进程使用超过GPU物理可用内存的内存。这可能导致性能下降。
TQ仅在启用调度器的反颠簸模式时生效。
较大的时间量子会牺牲交互性(响应性)来换取吞吐量(利用率)。
调度器的TQ决定了调度器分配GPU给客户端的时间长度。较大的时间量子会牺牲交互性(延迟)来换取吞吐量(利用率),反之亦然。
不应将时间量子设置得太小(< 10),因为刚获得GPU锁的应用程序获取页面所花费的时间需要几秒钟,因此它没有足够的时间进行实际计算。
为了最小化一组连续(批处理)作业的总完成时间,你可以将TQ设置为很大的值。
没有nvshare
时,你会耗尽内存,不得不一个接一个地运行作业。
使用nvshare
时:
nvshare
基于我的学位论文"交互式机器学习开发中图形处理单元高效利用的动态内存管理",该论文于2021年7月发表,可在http://dx.doi.org/10.26240/heal.ntua.21988 获取。
标题和第一部分是希腊语,但第二部分是完整的英文论文。你也可以在此仓库根目录下的grgalex-thesis.pdf
中找到它。
查看关于nvshare
的演示:
nvshare
。(可选)从Releases
标签下载最新发布的tarball,或通过命令行:
wget https://github.com/grgalex/nvshare/releases/download/v0.1.1/nvshare-v0.1.1.tar.gz -O nvshare.tar.gz
解压tarball:
tar -xzvf nvshare.tar.gz
安装libnvshare.so
并更新动态链接器的缓存:
sudo mv libnvshare.so /usr/local/lib/libnvshare.so && \ sudo ldconfig /usr/local/lib
安装nvshare-scheduler
:
nvshare
使用UNIX套接字进行通信,并将它们存储在/var/run/nvshare
下,所以必须以root身份运行。
sudo mv nvshare-scheduler /usr/local/sbin/nvshare-scheduler
安装nvsharectl
:
sudo mv nvsharectl /usr/local/bin/nvsharectl
删除tarball:
rm nvshare.tar.gz
启动nvshare-scheduler
:
必须以
root
身份运行,所以我们必须使用sudo
。
nvshare-scheduler
可执行文件将:
/var/run/nvshare
目录/var/run/nvshare/scheduler.sock
UNIX套接字nvshare
客户端的请求。选项A:以正常日志启动nvshare-scheduler
:
sudo bash -c 'nvshare-scheduler'
选项B:以调试日志启动nvshare-scheduler
:
sudo bash -c 'NVSHARE_DEBUG=1 nvshare-scheduler'
[故障排除]:如果你收到以下错误:
nvshare-scheduler: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.34' not found (required by nvshare-scheduler)
那么你必须为你的系统从源代码构建nvshare
并重新安装。
使用LD_PRELOAD
启动你的应用程序:
我们使用
LD_PRELOAD
将自定义nvshare
逻辑注入到CUDA应用程序中。libnvshare
自动检测它是否在CUDA应用程序中运行,只有在这种情况下才与nvshare-scheduler
通信。
选项A:导出LD_PRELOAD
变量:
export LD_PRELOAD=libnvshare.so
然后您可以像平常一样启动您的CUDA应用程序。
选项B:为单个程序设置LD_PRELOAD
环境变量:
在程序前加上LD_PRELOAD
指令,然后像平常一样启动您的程序。
LD_PRELOAD=libnvshare.so <您的程序> <您的参数>
选项C:在/etc/ld.so.preload
中为libnvshare.so
添加一个条目:
在某些情况下,例如使用Jupyter Notebook服务器时,可能很难为其启动后生成的Notebook设置环境变量。在这些情况下,您可以选择使用
ld.so.preload
文件。
sudo bash -c 'echo -ne "\n/usr/local/lib/libnvshare.so" >> /etc/ld.so.preload'
nvsharectl
配置nvshare-scheduler
:默认情况下,nvshare-scheduler
是开启的。这意味着在TQ秒内,只有一个进程在GPU上运行计算。
用法: nvsharectl [选项] 一个用于配置nvshare调度器的命令行工具。 -T, --set-tq=n 将调度器的时间量子设置为TQ秒。仅接受正整数。 -S, --anti-thrash=s 设置调度器的所需状态。仅接受"on"或"off"