写入共享内存制作副本?
Writing to Shared Memory Making Copies?
我正在尝试并行化一个程序,该程序使用共享内存通过网络读取分块的 numpy 数组。它似乎工作(我的数据从另一边出来),但是我所有子进程的内存都在膨胀到大约共享内存的大小(每个大约 100-250MB),当我写入它时会发生这种情况。有什么方法可以避免创建这些副本吗?它们似乎是不必要的,因为数据正在传播回实际的共享内存阵列。
以下是我使用 posix_ipc、mmap 和 numpy (np) 设置数组的方法:
shared = posix_ipc.SharedMemory(vol.uuid, flags=O_CREAT, size=int(nbytes))
array_like = mmap.mmap(shared.fd, shared.size)
renderbuffer = np.ndarray(buffer=array_like, dtype=vol.dtype, shape=mcshape)
当我这样做时内存增加:
renderbuffer[ startx:endx, starty:endy, startz:endz, : ] = 1
感谢您的帮助!
您的实际数据有 4 个维度,但我将处理一个更简单的 2D 示例。
假设你有这个数组 (renderbuffer
):
1 2 3 4 5
6 7 8 9 10
11 12 13 14 15
16 17 18 19 20
现在想象一下您的 startx/endx/starty/endy 参数 select 这个切片在一个过程中:
8 9
13 14
18 19
整个数组是 4x5 乘以 8 字节,所以 160 字节。 "window" 是 3x2 乘以 8 个字节,所以是 48 个字节。
您的期望似乎是访问这个 48 字节的切片在这个进程中需要 48 字节的内存。但实际上它需要接近完整的 160 个字节。为什么?
原因是内存是按页映射的,一般每页4096字节。因此,当您访问第一个元素(此处为数字 8)时,您将映射包含该元素的整个 4096 字节页面。
许多系统上的内存映射保证从页面边界开始,因此数组中的第一个元素将位于页面的开头。因此,如果您的数组为 4096 字节或更小,则访问其中的任何元素都会将整个数组映射到每个进程的内存中。
在您的实际用例中,您在切片中访问的每个元素都会导致映射包含它的整个页面。内存中相邻的元素(意味着切片中的第一个或最后一个索引递增一个,具体取决于您的数组 order
)通常位于同一页中,但在其他维度中相邻的元素可能位于单独的页面。
但请放心:内存映射在进程之间共享,因此如果您的整个数组为 200 MB,即使每个进程最终将映射大部分或全部,总内存使用量仍然为 200 MB所有过程。许多内存测量工具会报告每个进程使用 200 MB,这是真实但无用的:它们共享同一内存的 200 MB 视图。
我正在尝试并行化一个程序,该程序使用共享内存通过网络读取分块的 numpy 数组。它似乎工作(我的数据从另一边出来),但是我所有子进程的内存都在膨胀到大约共享内存的大小(每个大约 100-250MB),当我写入它时会发生这种情况。有什么方法可以避免创建这些副本吗?它们似乎是不必要的,因为数据正在传播回实际的共享内存阵列。
以下是我使用 posix_ipc、mmap 和 numpy (np) 设置数组的方法:
shared = posix_ipc.SharedMemory(vol.uuid, flags=O_CREAT, size=int(nbytes))
array_like = mmap.mmap(shared.fd, shared.size)
renderbuffer = np.ndarray(buffer=array_like, dtype=vol.dtype, shape=mcshape)
当我这样做时内存增加:
renderbuffer[ startx:endx, starty:endy, startz:endz, : ] = 1
感谢您的帮助!
您的实际数据有 4 个维度,但我将处理一个更简单的 2D 示例。
假设你有这个数组 (renderbuffer
):
1 2 3 4 5
6 7 8 9 10
11 12 13 14 15
16 17 18 19 20
现在想象一下您的 startx/endx/starty/endy 参数 select 这个切片在一个过程中:
8 9
13 14
18 19
整个数组是 4x5 乘以 8 字节,所以 160 字节。 "window" 是 3x2 乘以 8 个字节,所以是 48 个字节。
您的期望似乎是访问这个 48 字节的切片在这个进程中需要 48 字节的内存。但实际上它需要接近完整的 160 个字节。为什么?
原因是内存是按页映射的,一般每页4096字节。因此,当您访问第一个元素(此处为数字 8)时,您将映射包含该元素的整个 4096 字节页面。
许多系统上的内存映射保证从页面边界开始,因此数组中的第一个元素将位于页面的开头。因此,如果您的数组为 4096 字节或更小,则访问其中的任何元素都会将整个数组映射到每个进程的内存中。
在您的实际用例中,您在切片中访问的每个元素都会导致映射包含它的整个页面。内存中相邻的元素(意味着切片中的第一个或最后一个索引递增一个,具体取决于您的数组 order
)通常位于同一页中,但在其他维度中相邻的元素可能位于单独的页面。
但请放心:内存映射在进程之间共享,因此如果您的整个数组为 200 MB,即使每个进程最终将映射大部分或全部,总内存使用量仍然为 200 MB所有过程。许多内存测量工具会报告每个进程使用 200 MB,这是真实但无用的:它们共享同一内存的 200 MB 视图。