为什么multiprocessing.sharedctypes分配这么慢?

发布于 2021-01-29 17:15:24

以下是一些基准测试代码来说明我的问题:

import numpy as np
import multiprocessing as mp
# allocate memory
%time temp = mp.RawArray(np.ctypeslib.ctypes.c_uint16, int(1e8))
Wall time: 46.8 ms
# assign memory, very slow
%time temp[:] = np.arange(1e8, dtype = np.uint16)
Wall time: 10.3 s
# equivalent numpy assignment, 100X faster
%time a = np.arange(1e8, dtype = np.uint16)
Wall time: 111 ms

基本上,我希望在多个进程之间共享一个numpy数组,因为它很大且是只读的。这种方法效果很好,不需要额外的副本,并且进程的实际计算时间也不错。但是 创建 共享阵列的开销很大。

这篇文章提供了一些很好的见解,说明为什么初始化数组的某些方式很慢(请注意,在上面的示例中,我使用的是更快的方法)。但是这篇文章并没有真正描述如何真正提高像表演这样的速度。

有人对提高速度有任何建议吗?一些cython代码对分配数组有意义吗?

我正在Windows 7 x64系统上工作。

关注者
0
被浏览
46
1 个回答
  • 面试哥
    面试哥 2021-01-29
    为面试而生,有面试问题,就找面试哥。

    由于第二个链接中给出的原因,这很慢,并且解决方案实际上非常简单:
    绕过(慢速)RawArray切片分配代码,在这种情况下,该 代码
    每次从源数组中低效地读取一个原始C值来创建一个Python对象,然后将其直接转换回原始C,以存储在共享数组中,然后丢弃临时Python对象,并重复1e8次数。

    但是您不需要那样做;与大多数C级事物一样,它RawArray实现了缓冲区协议,这意味着您可以将其转换memoryview为底层原始内存的视图,该视图以类似于C的方式实现大多数操作,并尽可能使用原始内存操作。因此,与其做:

    # assign memory, very slow
    %time temp[:] = np.arange(1e8, dtype = np.uint16)
    Wall time: 9.75 s  # Updated to what my machine took, for valid comparison
    

    用于memoryview将其作为类似于原始字节的对象进行操作并进行分配(np.arange已经实现了缓冲区协议,并且memoryview的slice分配运算符无缝使用它):

    # C-like memcpy effectively, very fast
    %time memoryview(temp)[:] = np.arange(1e8, dtype = np.uint16)
    Wall time: 74.4 ms  # Takes 0.76% of original time!!!
    

    注意,后者的时间是毫秒,而不是秒;使用memoryview包装进行复制以执行原始内存传输只需不到1%的时间即可完成RawArray,默认情况下采用折叠方式即可!



知识点
面圈网VIP题库

面圈网VIP题库全新上线,海量真题题库资源。 90大类考试,超10万份考试真题开放下载啦

去下载看看