如何在不复制的情况下在多个进程中使用大型数据集?

Posted

技术标签:

【中文标题】如何在不复制的情况下在多个进程中使用大型数据集?【英文标题】:How to use large dataset in multiple processes without copying it? 【发布时间】:2019-12-26 14:38:28 【问题描述】:

我需要在 python 中对大型数据集 (>10GB) 运行多个 (1000-10000s) 搜索查询。为了加快速度,我想并行运行各个查询。但是,据我了解,将数据集解析到不同的进程会复制它,从而增加内存需求,这很快就会变得不可行。所以,想问问社区,是否可以在不增加内存占用的情况下,在多个并行运行的进程(函数)中解析(处理)一个大数据集?

下面是一个示例脚本。在这里,随着n 的增加,内存使用量会增加并很快变得受限。

from multiprocessing import Pool
import sys

def goo(d):
    for k,v in d.items():
        print(k,len(v))

d = 'a':[3]*(10**5),
     'b':[6]*(10**8)

n = int(sys.argv[1])

with Pool(processes=n) as pool:
    pool.starmap(goo, [[d]]*n)

编辑: 澄清一下,这是一个工具的一部分,将与在不同平台和环境中工作的其他人共享。因此,我需要一些特定于 python 的东西,因为我不想让解决方案依赖于外部依赖项。

【问题讨论】:

对于这么大的数据集,您应该考虑设置一个旨在处理规模的框架。 mysql 或 Apache Arrow 提取 parquet 文件将是一个不错的起点。 但是你事先有一个大数据集,它不是通过n arg 动态累积的,对吗? @James 这是将与其他人共享的工具的一部分。所以,为了避免依赖,我需要一些基于 python 的解决方案。 【参考方案1】:

我使用字典的global 变量解决了我的问题。

from multiprocessing import Pool
import sys

def goo(i):
    global d
    for k,v in d.items():
        print(k,len(v))

d = 'a':[3]*(10**5),
     'b':[6]*(10**8)

n = int(sys.argv[1])

with Pool(processes=n) as pool:
    pool.map(goo, range(n))

我不知道为什么会这样,因为我在多个地方读到每个进程都获得主内存的“副本”,但使用 global 变量似乎没有复制字典。

【讨论】:

【参考方案2】:

第一个选项是使用Manager,它允许您跨进程共享您的字典。请参阅this 示例了解如何执行此操作。

您的第二个选择是使用队列而不是数据创建流程。这是我处理大数据的首选方式。背后的想法是,数据仅由主线程访问,主线程将数据输入任务队列,然后由子进程从中提取数据。这在某种程度上是Pool.map() 正在做的事情,但是使用队列它消耗的内存要少得多,而且速度同样快。实现这一点需要更多的代码,但你有更多的控制权。 Here 是一个关于如何做到这一点的例子。

【讨论】:

【参考方案3】:

如果您已经在内存中拥有数据集并且您希望避免将副本发送到其他进程,只需使用 shared memory 作为 IPC mechanism(如果它在您的操作系统中可用)。

谷歌搜索 python shmgetshmget 是 Linux 中的系统调用)会生成这个库,可能对您有用 sysv_ipc

【讨论】:

以上是关于如何在不复制的情况下在多个进程中使用大型数据集?的主要内容,如果未能解决你的问题,请参考以下文章

如何在不使用单例的情况下在多个视图控制器之间传递数据?

如何在不重复的情况下在进程之间共享大量数据? (IPC)

如何在不复制/粘贴的情况下在我的所有活动中使用这些元素?

如何在不使用 System.Net 类的情况下在 C# 中发送 HTTP 请求? [复制]

如何在不使用 JSON.stringify 或 JSON.parse 的情况下在 javascript 中克隆数组? [复制]

如何在不使用大型结果集的情况下检查一个字符串是不是是另一个字符串的子字符串?