加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > Python > 正文

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

发布时间:2020-12-20 11:41:13 所属栏目:Python 来源:网络整理
导读:我试图了解在不同进程之间共享相同数据源的最有效和耗费更少内存的方法. 想象一下以下代码,简化了我的问题. import pandas as pdimport numpy as npfrom multiprocessing import Pool# method #1def foo(i): return data[i]if __name__ == '__main__': data
我试图了解在不同进程之间共享相同数据源的最有效和耗费更少内存的方法.

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

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),8),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,(data1,1)])

第三种方法,而不是传递所有“数据”,因为我们知道我们将只使用第一个记录,我只传递前1000条记录.这会有什么不同吗?

背景
我面临的问题是我有一个大约2百万行(内存为4GB)的大数据集,然后由四个子进程进行一些详细说明.每个细化仅影响一小部分数据(20000行),并且我希望最小化每个并发进程的内存使用.

解决方法

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

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

使用第三种方法,您只需传递比方法二更小的数据结构.这应该会更好,因为您不需要挑选/取消尽可能多的数据.

另一个注意事项 – 多次传递数据绝对是个坏主意,因为每个副本都会反复进行pickle / unpickled.你想把它传递给每个孩子一次.方法1是一种很好的方法,或者您可以使用initializer关键字参数将数据显式传递给子级.这将在Linux上使用fork并在Windows上进行pickling以将数据传递给子进程:

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,1])

使用第一种方法,您可以利用fork的行为来允许子进程继承数据对象. fork具有写时复制语义,这意味着内存实际上是在父项及其子项之间共享,直到您尝试在子项中写入它.当您尝试写入时,必须复制包含您要写入的数据的内存页面,以使其与父版本分开.

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

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读