在Python中写入共享内存非常慢

时间:2017-06-25 14:05:31

标签: python linux performance numpy shared-memory

我使用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 ,在初始化和写入数组时,有一个与共享数组大小相同的磁盘写入活动,但我不确定是否创建了一个真实文件,或者它只是在 - 记忆操作(我想它应该是)。通常,在大型共享阵列的情况下,我的系统响应性也会降低。没有交换,使用topipcs -muvmstat进行了核对。

2 个答案:

答案 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)