numactl 内存绑定(membind) 失效

107 阅读3分钟

前一篇帖子总结了,numactl 的相关内容,《并行AI worklaod numactl 优化

这篇帖子主要分析在实际优化 AI workload 的过程中遇到的具体问题。 还是承接上一篇帖子的 numactl command line

parallel --lb -d, --tagstring "[{#}]":::\
"numactl -C 0~1 -m 0  python main.py -a resnet50 -e -b 1 --pretrained --gpu0 0$DATASETS--int8 1 --benchmark 1 --num-iterations 5000 --dummy"\
"numactl -C 2~3 -m 0  python main.py -a resnet50 -e -b 1 --pretrained --gpu1 0$DATASETS--int8 1 --benchmark 1 --num-iterations 5000 --dummy"\
"numactl -C 4~5 -m 0  python main.py -a resnet50 -e -b 1 --pretrained --gpu3 0$DATASETS--int8 1 --benchmark 1 --num-iterations 5000 --dummy"\
"numactl -C 6~7 -m 0  python main.py -a resnet50 -e -b 1 --pretrained --gpu4 0$DATASETS--int8 1 --benchmark 1 --num-iterations 5000 --dummy"

当我按照上述 command line 启动这四个进程时,期望的现象是所有的 memory usage 都是在 Node0 domain, 毕竟 -C -m 都明显指定了 Node0。

但当进程启动之后,通过 numastat 检测了一下进程的状态,顿时傻眼了。。。。

image.png

也就是说这四个 python 还是使用了位于 Node1 domain 的内存资源,这样还是出现了跨 NUMA NODE 的资源访问,这样必然效率不是最高的。

我们来调查一下这个在 Node 1 的内存属于进程的哪一部分?为什么存在 Node 1 的资源调度? 以及如何避免这类 Node 1 的资源调度?

cat /proc/3685302/numa_maps image.png

原因其实是比较清晰的。上述代码段对应的内存,在Linux内核中,都属于有文件背景的页面,受 page cache机制管理。

也就是系统本身加载了一些动态库,该部分库的代码段可能进入到了 numa节点 1,从而在内存命中。接下来,如果我们用numactl -m 0 ./pyhton 去运行并绑定numa 节点 0,势必要再次需要 pyhto n的代码段以及 pyhton 依赖的动态库的代码段。但这些代码段都进入了page cache (位于NUMA node 1) ,所以这次在 numa node 0 运行的时候,其实是命中了numa node 1 里面的内存。

假设我们运行4个 a.out,这4个 a.out 分别运行于4个不同的 numa ,然后 a.out依赖 a.out 的代码段、libx.so代码段,liby.so 代码段。那么,完全有可能出现下图的情况,a.out 的代码段位于numa0,libcx.so 代码段位于 numa1 ,liby.so的代码段位于 numa2 ,这样4份运行中的 a.out ,都各自有跨 NUMA 的代码段内存访问,这样在 icach e替换的时候,都需要跨NUMA访问内存。

image.png

内核为什么这样做呢?原因在于,page cache的管理机制是以 inode 为单位的,每个 page inode 唯一, 一个inode(比如 a.out 对应的 inode)的page cache在内存命中的情况下,内核会直接用这部分page cache。这个page cache,不会为每个NUMA单独复制一份。从page cache的管理角度来讲, 这样减少 page fault 以及内存的占用。

如何避免 numactl 内存绑定(membind) 失效

1)Drop cache 推荐

sudo echo 3 > /proc/sys/vm/drop_caches + warm reboot 既然存在 page cache 的情况,那我们把 cache 清掉好了,清掉之后再重跑 AI workload 果然没有 Node 1 上的 memory usage 了。

当然这种方法不一定会完全清除 page cache ,但是能避免绝大部分的跨 Node 数据传输。

image.png

查看 numa_maps 也没有 N1 的资源了。

通过上述优化,我发现 AI inference 有 +3%~+7% 性能的提升~~~

2)per-numa Page cache

目前 linux kernel 并不支持这类 Page Cahe, 但已经有一个 Kernel PR 并没有 merge 到 kernel 之中。主要的缺点是太消耗内存资源了,基本就是用空间换时间。 git.kernel.org/pub/scm/lin…

这样让每个NUMA里面的外设申请连续内存的时候,可以申请到本NUMA的近地址内存,而不用跑到远端去,从而提高I/O的性能:

image.png

考虑到代码段以及其他page cache的跨NUMA特点。内核可以支持让关键的代码段,文件背景页面,在每个NUMA单独获得一份page cache:

image.png

3)内核感知到 a.out 独一份,没有 shar e的情况,直接在内核态把 page cache 直接 migrate 到 numa0, 也就是就近搬移