我使用python.multiprocessing.sharedctypes.RawArray
在多个进程之间共享大型numpy数组。我注意到,当这个数组很大(> 1或2 Gb)时,初始化变得非常慢,读取/写入速度也慢得多(并且读取/写入时间不可预测,有时非常快,有时甚至非常快很慢)。
我制作了一个小样本脚本,只使用一个进程,初始化共享数组并多次写入。并测量进行这些操作的时间。
import argparse
import ctypes
import multiprocessing as mp
import multiprocessing.sharedctypes as mpsc
import numpy as np
import time
def main():
parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument('-c', '--block-count', type=int, default=1,
help='Number of blocks to write')
parser.add_argument('-w', '--block-width', type=int, default=20000,
help='Block width')
parser.add_argument('-d', '--block-depth', type=int, default=15000,
help='Block depth')
args = parser.parse_args()
blocks = args.block_count
blockwidth = args.block_width
depth = args.block_depth
start = time.perf_counter()
shared_array = mpsc.RawArray(ctypes.c_uint16, blocks*blockwidth*depth)
finish = time.perf_counter()
print('Init shared array of size {:.2f} Gb: {:.2f} s'.format(blocks*blockwidth*depth*ctypes.sizeof(ctypes.c_uint16)/1024/1024/1024, (finish-start)))
numpy_array = np.ctypeslib.as_array(shared_array).reshape(blocks*blockwidth, depth)
start = time.perf_counter()
for i in range(blocks):
begin = time.perf_counter()
numpy_array[i*blockwidth:(i+1)*blockwidth, :] = np.ones((blockwidth, depth), dtype=np.uint16)
end = time.perf_counter()
print('Write = %.2f s' % (end-begin))
finish = time.perf_counter()
print('Total time = %.2f s' % (finish-start))
if __name__ == '__main__':
main()
当我运行此代码时,我在PC上获得以下内容:
$ python shared-minimal.py -c 1
Init shared array of size 0.56 Gb: 0.36 s
Write = 0.13 s
Total time = 0.13 s
$ python shared-minimal.py -c 2
Init shared array of size 1.12 Gb: 0.72 s
Write = 0.12 s
Write = 0.13 s
Total time = 0.25 s
$ python shared-minimal.py -c 4
Init shared array of size 2.24 Gb: 5.40 s
Write = 1.17 s
Write = 1.17 s
Write = 1.17 s
Write = 1.57 s
Total time = 5.08 s
在最后一种情况下,当数组大小超过2 Gb时,初始化时间不依赖于数组大小,并且将保存大小切片分配给数组的速度要慢5倍以上。
我想知道为什么会这样。我正在使用Python 3.5在Ubuntu 16.04上运行脚本。我还注意到使用 iotop ,在初始化和写入数组时,有一个与共享数组大小相同的磁盘写入活动,但我不确定是否创建了一个真实文件,或者它只是在 - 记忆操作(我想它应该是)。通常,在大型共享阵列的情况下,我的系统响应性也会降低。没有交换,使用top
,ipcs -mu
和vmstat
进行了核对。
答案 0 :(得分:1)
经过更多研究后我发现python实际上在/tmp
中创建了以pymp-
开头的文件夹,尽管使用文件查看器在其中没有文件可见,但它看起来很像{{1 python用于共享内存。刷新文件内容时,性能似乎在下降。
最终的工作解决方案是将/tmp/
安装为/tmp
:
tmpfs
并且,如果使用最新的docker,则通过向sudo mount -t tmpfs tmpfs /tmp
命令提供--tmpfs /tmp
参数。
执行此操作后,读/写操作在RAM中完成,性能快速稳定。
我仍然想知道为什么docker run
用于共享内存,而不是/tmp
,它已被列为/dev/shm
并且应该用于共享内存。
答案 1 :(得分:1)
你可以试试 np.frombuffer
,这在我的测试中要快得多。
只需替换以下行
numpy_array = np.ctypeslib.as_array(shared_array).reshape(blocks*blockwidth, depth)
与
numpy_array = np.frombuffer(shared_array, dtype=np.uint16).reshape(blocks*blockwidth, depth)