Python多进程共享内存与使用参数

Python多进程共享内存与使用参数,python,pandas,dataset,multiprocessing,shared-memory,Python,Pandas,Dataset,Multiprocessing,Shared Memory,我试图弄清楚,在不同进程之间共享同一数据源的最有效、最节省内存的方法是什么 想象一下下面的代码,它简化了我的问题 import pandas as pd import numpy as np from multiprocessing import Pool # method #1 def foo(i): return data[i] if __name__ == '__main__': data = pd.Series(np.array(range(100000))) pool

我试图弄清楚,在不同进程之间共享同一数据源的最有效、最节省内存的方法是什么

想象一下下面的代码,它简化了我的问题

import pandas as pd
import numpy as np
from multiprocessing import Pool

# method #1
def foo(i): return data[i]
if __name__ == '__main__':
    data = pd.Series(np.array(range(100000)))
    pool = Pool(2)
    print pool.map(foo,[10,134,8,1])

# method #2
def foo((data,i)): return data[i]
if __name__ == '__main__':
    data = pd.Series(np.array(range(100000)))
    pool = Pool(2)
    print pool.map(foo,[(data,10),(data,134),(data,8),(data,1)])
在第一种方法中,将使用全局变量(在Windows上不起作用,仅在Linux/OSX上起作用),然后通过函数访问该变量。在第二个方法中,我将“数据”作为参数的一部分传递

就过程中使用的内存而言,这两种方法会有区别吗

# method #3
def foo((data,i)): return data[i]
if __name__ == '__main__':
    data = pd.Series(np.array(range(100000)))
    pool = Pool(2)
    # reduce the size of the argument passed
    data1 = data[:1000]
    print pool.map(foo,[(data1,10),(data1,134),(data1,8),(data1,1)])
第三种方法,不是传递所有的“数据”,因为我们知道我们将只使用第一条记录,我只传递前1000条记录。这会有什么不同吗

背景
我面临的问题是,我有一个大约200万行(内存为4GB)的大数据集,它将通过四个子进程进行细化。每次细化只影响一小部分数据(20000行),我希望尽量减少每个并发进程的内存使用。

我将从第二种和第三种方法开始,因为它们更容易解释

将参数传递到
pool.map
pool.apply
时,参数将被pickle,使用管道发送到子进程,然后在子进程中取消pickle。当然,这需要传递的数据结构的两个完全不同的副本。它还可能导致大型数据结构的性能降低,因为对大型对象进行酸洗/取消酸洗可能需要相当长的时间

使用第三种方法,只需传递比方法二更小的数据结构。这应该表现得更好,因为您不需要pickle/unpickle那么多数据

另一个注意事项——多次传递
数据
肯定是个坏主意,因为每个副本都会被重复地酸洗/取消酸洗。你想把它传给每个孩子一次。方法1是一种很好的方法,或者您可以使用
初始值设定项
关键字参数显式地将
数据
传递给子级。这将在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
的行为来允许子进程继承
数据
对象
fork
具有写时复制语义,这意味着内存实际上在父级及其子级之间共享,直到您尝试在子级中写入它为止。当您尝试写入时,必须复制包含您尝试写入的数据的内存页,以使其与父版本分开


现在,这听起来像一个扣篮——只要我们不写,就不需要复制任何东西,这肯定比pickle/unpickle方法快。通常情况就是这样。然而,在实践中,Python在内部写入其对象,即使您并不真正期望它这样做。因为Python使用引用计数进行内存管理,所以每次将对象传递给方法或分配给变量等时,它都需要增加每个对象上的内部引用计数器。因此,这意味着包含传递给子进程的每个对象的引用计数的内存页最终将被复制。这肯定会比多次酸洗
数据更快,占用的内存更少,但也不是完全共享的。

我遇到了这个错误:
-->17 print pool.map(foo[10134,8,1],initializer=init,initargs=(数据,))TypeError:map()得到了一个意外的关键字参数“initializer”
多处理。\uuuu版本:“0.70a1”
谢谢-解释得很好,这将满足我的需要@首先,我将
初始值设定项
参数添加到了错误的行中。它属于对
池(…)
的调用,而不是对
映射的调用。我还把
初始值设定项的拼写打错了。这就是我从内存中编写代码的结果:)。不管怎样,现在修好了。