NVidia-Docker2 性能优化

用户头像
薛磊
关注
发布于: 2020 年 04 月 22 日

前面介绍了NVidia-docker的运行原理,那么接下来就是我们要考虑的重点了——启动性能。



在我的测试环境中,对比了使用nvidia-runtime启动容器与使用runc启动容器的启动延时:



  • 使用runc启动一个nginx容器,耗时在400~600ms不等

  • 使用nvidia-runtime启动一个nginx容器,耗时在1.3~1.5s不等



对比发现,差距还是很明显的,好奇问题发生在哪里?下面我们就来一探究竟。



打开日志



为了能够更好的查看各个组件的时间消耗,我们需要将日志级别调高以便能够清楚各个组件的时间消耗。



Docker daemon, 修改配置文件/etc/docker/daemon.json中的,设置"debug": true, "log-level": "debug",

Containerd, 修改配置文件 /etc/containerd/config.toml,设置[debug] address = "/run/containerd/debug.sock" # uid = 0 # gid = 0 level = "trace" [plugins] [plugins.linux] shim_debug = true

NVidia container runtime, 修改配置文件 /etc/nvidia-container-runtime/config.toml,设置

[nvidia-container-cli] debug = "/var/log/nvidia-container-runtime-hook.log" [nvidia-container-runtime] debug = "/var/log/nvidia-container-runtime.log"



分析问题



  • 通过nvidia-docker创建并运行一个GPU容器

  • 分别查看各个组件的日志

  • 通过docker+runc创建并运行一个普通容器

  • 分别查看各个组件的日志



对比两个容器的创建和运行过程,发现nvidia-container-cli耗时相对是最长的。而这个组件也是只有GPU容器才会运行的,由文档中所介绍所述,该组件主要是为了我们所创建的普通容器增加额外的NVidia驱动、软件库以及相应的命令。



查看该组件的日志:



-- WARNING, the following logs are for debugging purposes only --
I0114 10:01:57.797152 27834 nvc.c:281] initializing library context (version=1.0.2, build=49029392a1847ef7e5e14903a046373c9c053c0c)
I0114 10:01:57.797188 27834 nvc.c:255] using root /
I0114 10:01:57.797193 27834 nvc.c:256] using ldcache /etc/ld.so.cache
I0114 10:01:57.797197 27834 nvc.c:257] using unprivileged user 65534:65534
I0114 10:01:57.802744 27843 nvc.c:191] loading kernel module nvidia
I0114 10:01:57.803027 27843 nvc.c:203] loading kernel module nvidia_uvm
I0114 10:01:57.803116 27843 nvc.c:211] loading kernel module nvidia_modeset
E0114 10:01:57.809016 27843 nvc.c:213] could not load kernel module nvidia_modeset
I0114 10:01:57.809185 27845 driver.c:133] starting driver service
I0114 10:01:58.258515 27834 nvc_container.c:364] configuring container with 'video compute utility supervised'
I0114 10:01:58.258769 27834 nvc_container.c:384] setting pid to 27826
I0114 10:01:58.258776 27834 nvc_container.c:385] setting rootfs to /data1/docker/overlay/b386cfd5e125decfbb3d64866a477b834df8a6581f5f592d87356729bb7590ce/merged
I0114 10:01:58.258780 27834 nvc_container.c:386] setting owner to 0:0
I0114 10:01:58.258785 27834 nvc_container.c:387] setting bins directory to /usr/bin
I0114 10:01:58.258797 27834 nvc_container.c:388] setting libs directory to /usr/lib64
I0114 10:01:58.258801 27834 nvc_container.c:389] setting libs32 directory to /usr/lib
I0114 10:01:58.258804 27834 nvc_container.c:390] setting cudart directory to /usr/local/cuda
I0114 10:01:58.258808 27834 nvc_container.c:391] setting ldconfig to @/sbin/ldconfig (host relative)
I0114 10:01:58.258811 27834 nvc_container.c:392] setting mount namespace to /proc/27826/ns/mnt
I0114 10:01:58.258815 27834 nvc_container.c:394] setting devices cgroup to /sys/fs/cgroup/devices/kubepods/podc3930388-36b4-11ea-ba67-246e963502c8/2c8ab17a8c34508b752272ce4f71ddcbbc6a9c5785c1896cabde6b2c024a46a1
I0114 10:01:58.258820 27834 nvc_info.c:434] requesting driver information with ''
I0114 10:01:58.258940 27834 nvc_info.c:148] selecting /usr/lib64/vdpau/libvdpau_nvidia.so.418.116.00
I0114 10:01:58.259043 27834 nvc_info.c:148] selecting /usr/lib64/libnvidia-tls.so.418.116.00
I0114 10:01:58.259065 27834 nvc_info.c:148] selecting /usr/lib64/libnvidia-ptxjitcompiler.so.418.116.00
I0114 10:01:58.259095 27834 nvc_info.c:148] selecting /usr/lib64/libnvidia-opticalflow.so.418.116.00
I0114 10:01:58.259123 27834 nvc_info.c:148] selecting /usr/lib64/libnvidia-opencl.so.418.116.00
I0114 10:01:58.259146 27834 nvc_info.c:148] selecting /usr/lib64/libnvidia-ml.so.418.116.00
I0114 10:01:58.259173 27834 nvc_info.c:148] selecting /usr/lib64/libnvidia-ifr.so.418.116.00
I0114 10:01:58.259202 27834 nvc_info.c:148] selecting /usr/lib64/libnvidia-glsi.so.418.116.00
I0114 10:01:58.259221 27834 nvc_info.c:148] selecting /usr/lib64/libnvidia-glcore.so.418.116.00
...
I0114 10:01:58.317203 27834 nvc_mount.c:320] mounting /proc/driver/nvidia/gpus/0000:e7:00.0 at /data1/docker/overlay/b386cfd5e125decfbb3d64866a477b834df8a6581f5f592d87356729bb7590ce/merged/proc/driver/nvidia/gpus/0000:e7:00.0
I0114 10:01:58.317218 27834 nvc_mount.c:357] whitelisting device node 195:7
I0114 10:01:58.317232 27834 nvc_ldcache.c:344] executing /sbin/ldconfig from host at /data1/docker/overlay/b386cfd5e125decfbb3d64866a477b834df8a6581f5f592d87356729bb7590ce/merged
I0114 10:01:58.410697 27834 nvc.c:318] shutting down library context

研究上面的日志,可以发现有几点时间消耗比较长:

  • starting driver service 这个步骤消耗时间比较长,与下一个阶段花费了大概550ms

  • 结尾部分 terminating driver service这个步骤,大概花费了220ms



发现耗时比较长的地方,那么就需要研究代码了

  • starting driver service这个部分代码在这里,具体的调用过程,简单如下:



main(src/cli/configure.c)
+
|
v
nvc_init(src/nvc.c)
+
|
v
driver_init(src/driver.c)
+
|
v
setup_rpc_service(src/driver.c) +--> log_info("starting driver service") +--> ... +--> log_info("terminating driver service")



基于这个流程,我们不难发现日志中间花费了比较长的时间,主要就是设置了一个RPC-server,用来跟cuda以及nvml相关的驱动交互获取信息。



  • terminating driver service 这个部分代码在这里



优化



目前我们确认了耗时时间比较长的原因,那么我们如何优化呢?



一般特定场景都是有比较多的优化空间的,而我们这样的场景就是一个典型的特定场景



  • 所有机器都存在于IDC机房

  • 硬件偶尔可能会出错

  • 驱动定期会升级

  • 软件库版本会定期升级



也就是说上面分析的nvidia-container-cli 所查询的内容会变化,但是不是一直在变化的,因此基于这个出发点,我们可以制定优化策略:



  • 将NVidia GPU卡的型号、驱动软件以及库版本、地址等信息固化到一个配置文件中,nvidia-container-cli去文件中查询

  • 定期更新该信息文件



通过这种方式,预期可以节省500~800ms的查询时间。



解决



编译环境



# need docker to build the environment
$ git clone http://github/nvidia/libnvidia-container.git
$ cd libnvidia-container
$ make centos7-x86_64



最终会制作出RPM软件包,存在于dist目录中。



验证想法



为了能够快速验证想法,我将一台测试机器的硬件信息以及软件库版本信息硬编码到代码中,完成了一个初始版本,并在测试环境中验证。



结论



经过验证发现,启动一个GPU的容器,速度由原来的1.3~1.5s压缩到800ms左右,性能提升了40%多。



具体可以看下图,启动一个8卡的GPU容器时间(其中图中 Created是容器创建时间,StartedAt 是容器启动时间,通过这两个时间的差,可以看出启动速度):



修改之前





修改之后





展望



在分析过程中只是分析了主要的流程,很多其他微小的间隔,没有深入分析,但是还是有很大潜力的,后面会继续深入挖掘。



发布于: 2020 年 04 月 22 日 阅读数: 52
用户头像

薛磊

关注

知存储,懂容器,会炼丹 2019.07.10 加入

知存储,懂容器,会炼丹

评论

发布
暂无评论
NVidia-Docker2 性能优化