gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-12-04 22:45:30 31 4
gpt4 key购买 nike

我试图弄清楚什么是在不同进程之间共享相同数据源的最有效且内存消耗更少的方式。

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

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,使用管道发送到子进程,然后在子进程中unpickled。这当然需要您传递的数据结构的两个完全不同的副本。它还可能导致大型数据结构的性能下降,因为酸洗/取消酸洗大对象可能需要很长时间。

使用第三种方法,您只是传递比方法二更小的数据结构。这应该表现更好,因为您不需要腌制/取消腌制尽可能多的数据。

另一张纸条 - 路过 data多次绝对是一个坏主意,因为每个副本都会被反复腌制/取消腌制。你想把它传递给每个 child 一次。方法 1 是一个很好的方法,或者您可以使用 initializer显式传递的关键字参数 data给 child 。这将使用 fork在 Linux 上和在 Windows 上进行酸洗以将数据传递给子进程:

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具有 copy-on-write 语义,这意味着内存实际上在父级和它的子级之间共享,直到您尝试在子级中写入它。当您尝试写入时,您尝试写入的数据所在的内存页面必须被复制,以使其与父版本分开。

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

关于Python 多进程共享内存与使用参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28862115/

31 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com