分别在并行进程中改变不同的python对象

分别在并行进程中改变不同的python对象,python,fork,shared-memory,pickle,python-multiprocessing,Python,Fork,Shared Memory,Pickle,Python Multiprocessing,简而言之 我想同时更改复杂的python对象,这样每个对象只由一个进程处理。我如何才能做到这一点(最有效)?实施某种酸洗支持会有帮助吗?那会有效率吗 全部问题 我有一个python数据结构ArrayDict,它基本上由一个numpy数组和一个字典组成,并将任意索引映射到数组中的行。在我的例子中,所有键都是整数 a = ArrayDict() a[1234] = 12.5 a[10] = 3 print(a[1234]) #12.5

简而言之

我想同时更改复杂的python对象,这样每个对象只由一个进程处理。我如何才能做到这一点(最有效)?实施某种酸洗支持会有帮助吗?那会有效率吗

全部问题

我有一个python数据结构
ArrayDict
,它基本上由一个
numpy
数组和一个字典组成,并将任意索引映射到数组中的行。在我的例子中,所有键都是整数

a = ArrayDict()

a[1234] = 12.5
a[10] = 3

print(a[1234])                               #12.5
print(a[10])                                 # 3.0

print(a[1234] == a.array[a.indexDict[1234]]) #true
现在我有多个这样的
ArrayDict
s,我想把它们填入
myMethod(ArrayDict,params)
。因为
myMethod
很昂贵,所以我想并行运行它。请注意,
myMethod
可能会将许多行添加到
arrayDict
。每个进程都会改变自己的
ArrayDict
。我不需要同时访问
ArrayDict
s

myMethod
中,我更改
arrayDict
中的条目(即,我更改内部
numpy
数组),我将条目添加到
arrayDict
(即,我向字典添加另一个索引,并在内部数组中写入新值)。最后,我希望能够在
arrayDict
的内部
numpy
数组变得太小时交换它。这种情况并不经常发生,如果没有更好的解决方案,我可以在程序的非并行部分执行此操作。即使没有阵列交换,我自己的尝试也没有成功

我花了几天时间研究共享内存和python模块。因为我最终将在linux上工作,所以任务似乎相当简单:系统调用
fork()
允许有效地处理参数的副本。我当时的想法是在自己的过程中更改每个
ArrayDict
,返回对象的更改版本,并覆盖原始对象。为了节省内存和保存复制工作,我使用了额外的数组将数据存储在
ArrayDict
中。我知道这本词典仍需复制

from sharedmem import sharedmem
import numpy as np

n = ...                   # length of the data array
myData = np.empty(n, dtype=object)
myData[:] = [ArrayDict() for _ in range(n)]
done = False

while not done:
    consideredData = ...  # numpy boolean array of length
                          # n with True at the index of
                          # considered data
    args = ...            # numpy array containing arguments
                          # for myMethod

    with sharedmem.MapReduce() as pool:
        results = pool.map(myMethod, 
                           list(zip(myData[considered], 
                                    args[considered])),
                           star=True)
        myData[considered] = results

    done = ...             # depends on what happens in
                           # myMethod
我得到的是一个分段错误。我通过创建
ArrayDict
s到
myMethod
的深度副本,并将它们保存到
myData
中,从而绕过了这个错误。我真的不明白为什么这是必要的,而且频繁复制我的(可能非常大的)数组(while循环需要很长时间)对我来说似乎不是有效的。然而,至少在一定程度上它起了作用。然而,由于共享内存,我的程序在第三次迭代时出现了一些错误行为。因此,我认为我的方式不是最优的

我读到,可以使用
多处理.Array
在共享内存中保存aribtrary numpy数组。但是,我仍然需要共享整个
ArrayDict
,其中特别包括一个字典,而字典又是不可挑选的

我怎样才能有效地实现我的目标?是否有可能(且有效)以某种方式使我的对象可拾取

所有解决方案都必须在64位Linux上运行python 3和完整的numpy/scipy支持

编辑


我发现使用多处理“管理器”类和用户定义的代理类共享任意对象是可能的。这会有效率吗?我想利用这一点,我不需要并发访问对象,即使它们没有在主进程中处理。是否可以为我要处理的每个对象创建一个管理器?(我可能对管理者的工作方式仍有一些误解。)

这似乎是一个相当复杂的类,我无法完全预测此解决方案是否适用于您的情况。对于这样一个复杂的类,一个简单的折衷方法是使用

如果这不能回答您的问题,那么最好用一个最小的、有效的示例

from concurrent.futures import ProcessPoolExecutor

import numpy as np

class ArrayDict ():
  keys = None
  vals = None

  def __init__ (self):
    self.keys = dict ()
    self.vals = np.random.rand (1000)

  def __str__ (self):
    return "keys: " + str(self.keys) + ", vals: " + str(self.vals.mean())

def myMethod (ad, args):
  print ("starting:", ad)


if __name__ == '__main__':
  l     = [ArrayDict() for _ in range (5)]
  args  = [2, 3, 4, 1, 3]

  with ProcessPoolExecutor (max_workers = 2) as ex:

    d = ex.map (myMethod, l, args)
当对象被发送到子进程时,您需要返回结果(因为对对象的更改不会传播回主进程)并处理如何存储它们

请注意,对类变量的更改将传播到 相同的流程,例如,如果您的任务多于流程,则更改类 变量将在同一进程中运行的实例之间共享。这通常是不受欢迎的行为

这是并行化的高级接口
ProcessPoolExecutor
使用
多处理
模块,只能与一起使用。我怀疑
ProcessPoolExecutor
的性能类似于。在引擎盖下,
ProcessPoolExecutor
,应表现出与
Pool
类似的性能(与map一起使用时除外)
ProcessPoolExecutor
似乎是python中并发任务的预期未来API

如果可以,使用通常会更快(可以将其替换为
ProcessPoolExecutor
)。在这种情况下,对象在进程之间共享,对对象的更新将传播回主线程

如前所述,最快的选择可能是重新构造
ArrayDict
,以便它只使用可以由或
Array
表示的对象

如果
ProcessPoolExecutor
不起作用,并且您无法优化
ArrayDict
,则您可能无法使用。关于如何做到这一点,有很多很好的例子

最大的性能增益通常出现在
myMethod
中。 而且,正如我提到的,使用线程的开销要比进程的开销大


如何在myMethod中修改或使用arrayDict?(我想你的意思是
myMethod
不是
myFunc
?)@gauteh:谢谢你让我注意到打字错误。我纠正了它。我还添加了我如何在myMethod中修改arrayDict的描述