我将从第二和第三种方法开始,因为它们更容易解释。在
将参数传递给pool.map或pool.apply时,参数将被pickle,使用管道发送到子进程,然后在子进程中取消拾取。当然,这需要传递的数据结构的两个完全不同的副本。它还可能导致大数据结构的性能降低,因为pickle/unpickle大型对象需要相当长的时间。在
使用第三种方法,只需传递比方法2更小的数据结构。这应该会表现得更好,因为您不需要对太多的数据进行pickle/unpickle。在
另外一个注释-多次传递data绝对是个坏主意,因为每个副本都会反复地被pickle/unpickle。你想把它传给每个孩子一次。方法1是一种很好的方法,或者可以使用initializer关键字参数显式地将data传递给子对象。这将在Linux上使用fork,在Windows上使用pickle将数据传递给子进程:import pandas as pd
import numpy as np
from multiprocessing import Pool
data = None
def init(_data):
global data
data = _data # data is now accessible in all children, even on Windows
# method #1
def foo(i): return data[i]
if __name__ == '__main__':
data = pd.Series(np.array(range(100000)))
pool = Pool(2, initializer=init, initargs=(data,))
print pool.map(foo,[10,134,8,1])
使用第一种方法,您将利用fork的行为来允许子进程继承data对象。fork具有写时复制语义,这意味着内存实际上是在父级和子级之间共享的,直到您尝试在子级中写入内存。当您尝试写入时,必须复制您要写入的数据所在的内存页,以使其与父版本分开。在
现在,这听起来像一个灌篮-不需要复制任何东西,只要我们不写它,这肯定比pickle/unpickle方法快。通常都是这样。然而,在实践中,Python是在内部写入其对象,即使您并不期望它这样做。因为Python使用引用计数来管理内存,所以每次传递给方法或分配给变量等时,它都需要增加每个对象的内部引用计数器。因此,包含传递给子进程的每个对象的引用计数的内存页最终将被复制。与多次pickling data相比,这肯定更快,占用的内存更少,但也不是完全共享的。在