【发布时间】:2020-06-24 01:15:37
【问题描述】:
下面的脚本使用 mmap 并行写入内存映射数组。但是,它仅在所有进程都在同一个节点上时才有效 - 否则它会为不在 rank 0 节点上的处理器生成 0 行,或者输出中的其他杂散零。为什么是这样?我觉得我错过了 mmap 的工作原理。
编辑:在 NFS 系统和并行分布式系统上都会出现相同的结果。下面的评论者建议这与 mmap 的页面长度有关。当我的切片的“长度”正好是 4KiB 时,脚本仍然会产生错误的输出。当切片远大于 4 KiB 时也会发生同样的情况。
#!/usr/bin/python3
from mpi4py import MPI
import numpy as np
comm = MPI.COMM_WORLD
rank = comm.Get_rank()
size = comm.Get_size()
length = int(1e6) # Edited to make test case longer.
myfile = "/tmp/map"
if rank == 0:
fp = np.memmap(myfile, dtype=np.float32, mode='w+', shape=(size,length))
del fp
comm.Barrier()
fp = np.memmap(myfile, dtype=np.float32, mode='r+', shape=(1,length),
offset=rank*length*4)
fp[:,:] = np.full(length,rank)
comm.Barrier()
if rank == 0:
out = np.memmap(myfile, dtype=np.float32, mode='r', shape=(size,length))
print(out[:,:])
正确的输出:
[[ 0. 0. 0. 0.]
[ 1. 1. 1. 1.]
[ 2. 2. 2. 2.]
[ 3. 3. 3. 3.]
[ 4. 4. 4. 4.]]
输出不正确。等级 3 和 4 的处理器不写入。
[[ 0. 0. 0. 0.]
[ 1. 1. 1. 1.]
[ 2. 2. 2. 2.]
[ 0. 0. 0. 0.]
[ 0. 0. 0. 0.]]
【问题讨论】:
-
我假设
/tmp/map是一个本地文件,仅存在于运行 rank0的节点上。我很惊讶memmap在其他节点上没有失败(例如文件不存在)。 -
您缺少网络文件系统,例如 NFS(速度慢且通用,易于部署,非常受欢迎)或 Lustre(快速且并行,难以部署,主要用于 HPC)。跨度>
-
我已经在 NFS 和并行分布式文件系统上对此进行了测试,在这两种情况下都存在同样的问题。
-
这与内存映射在页面粒度上起作用的事实有关。页面通常不小于 4 KiB。如果您触摸页面中的单个字节,则整个页面被标记为脏,然后整个页面被刷新到磁盘。无论哪个进程最后通过网络刷新其修改的页面,都会覆盖其他进程所做的更改。当所有进程都在同一个主机上时,这不是问题,因为来自 FS 缓存的相同物理页面被映射到所有进程中。解决方案是不要映射这么小的区域,而是每个进程至少有 4 KiB。
-
请注意,即使您的数组是
1 x 4 x 4 = 16字节,内存映射的大小也会向上舍入到整个页面,并且也定位在页面大小边界上。
标签: numpy parallel-processing mmap