使用pool.map()时防止字典的副本

时间:2019-03-04 15:59:34

标签: python dictionary multiprocessing shared-memory

我有一个函数f(x),我想并行评估值列表xrange。该函数执行以下操作:

def f(x, wrange, dict1, dict2):

    out_list = []

    v1 = dict1[x]

    for w in wrange:
        v2 = dict2[x-w]
        out_list += [np.dot(v1, v2)]

    return out_list

它从字典dict1取值矩阵,从字典dict2取向量,然后将它们相乘。现在,我通常并行执行此操作的方法如下:

import functools
import multiprocessing

par_func = functools.partial(f, wrange=wrange, dict1=dict1, dict2=dict2)

p = multiprocessing.Pool(4)
ssdat = p.map(par_func, wrange)
p.close()
p.join()

现在,当dict1dict2是大词典时,这将导致代码失败并显示错误

File "/anaconda3/lib/python3.6/multiprocessing/connection.py", line 393, in _send_bytes header = struct.pack("!i", n)
struct.error: 'i' format requires -2147483648 <= number <= 2147483647

,我认为这是因为pool为我的函数的每次评估都复制了dict1dict2。有没有有效的方法来将这些词典设置为共享内存对象? map是执行此操作的最佳功能吗?

2 个答案:

答案 0 :(得分:2)

如果您使用的是基于fork的系统(阅读:不是Windows),此问题的一种解决方案是将dict s放在全局变量中,编写一个不包含以下内容的函数:不能将它们作为参数,而只是从其自身的全局变量访问它们并使用它。 functools.partial is, unfortunately, unsuited to this use case,但您的用例可轻松替换为全局变量和def版的功能:

import multiprocessing

# Assumes wrange/dict1/dict2 defined or imported somewhere at global scope,
# prior to creating the Pool
def par_func(x):
    return f(x, wrange, dict1, dict2)

# Using with statement implicitly terminates the pool, saving close/join calls
# and guaranteeing an exception while mapping doesn't leave the pool alive indefinitely
with multiprocessing.Pool(4) as p:
    ssdat = p.map(par_func, wrange)

在创建dict1之后,dict2 / Pool的更改将不会在进程之间反映,但是无论如何您似乎都以只读方式使用它,因此没问题。

如果您使用的是Windows,或者需要更改dict,则可以始终make a multiprocessing.Manager and make dict proxies with the dict method of the manager(这些共享的dict,在分配密钥时进行更新),但是比较难看而且速度较慢,因此,如果可能的话,我不建议这样做。

答案 1 :(得分:0)

如果要使用多处理在进程之间共享内存,则需要与multiprocessing.Array显式共享对象。这不是理想的,因为您想访问字典中的元素,并且找到正确的数据可能很耗时。如果确实成为您的问题,可能有一些解决方法。

如@Peque所述,另一种选择是使用threading。使用线程时,内存会在所有进程之间自动共享,但是由于global interpreter lock(GIL),您可能会遇到性能问题。 GIL是Python保持线程安全并避免出现竞争状况的方法。