为什么python中的子进程数会影响子进程的内存消耗?

时间:2013-01-16 09:59:01

标签: python

我在python中有一个非常有趣的案例。

主要流程创建N个子流程。 子进程是继承自 multiprocessing.Process

的类

现在,当子进程的数量是10时,每个子进程消耗大约15M的住宅内存。 但是,当我将子进程的数量增加到100时,每个子进程的住宅内存消耗都会跳到大约50M !!!

任何人都可以解释这种内存的跳跃/建议如何避免它?

以下是子流程类的结构:

class MySubProcess(multiprocessing.Process):
    def __init__(self, sub_process_number):
        multiprocessing.Process.__init__(self, target=self.go)

        self.m_sub_process_number = sub_process_number


    def go(self):
        self.m_config = global_config
        while (True):
        ....

非常感谢!!!

1 个答案:

答案 0 :(得分:1)

当我尝试一个简单的例子,其中每个子进程除了time.sleep()什么都不做,我没有看到这种行为,所以我不相信它是multiprocessing模块固有的东西。

我最好的猜测是fork()的内存复制功能,multiprocessing可能会在幕后使用。在Unix上分支新进程的语义要求将父进程的整个内存空间复制到子进程中。所以,假设您在启动这些MySubProcess结构之前创建了这些del结构的列表。然后将此列表复制到每个子进程的地址空间中,因此当您查看每个进程的驻留大小时,它将显得更大(假设您的结构占用了非常重要的内存量)。

此外,在启动子进程之前分配的任何其他内存都将被复制,但实例列表是我能想到的主要因素,当您分配更多进程时,这些内存会增加。根据您的代码,可能还有其他数据结构可以根据进程数量(例如工作队列)进行扩展。

如果fork()可能的每个孩子的上下文中你不需要的所有东西都会找回它们的大小,但这取决于Python分配器之间相当复杂的交互和系统内存分配器,所以这是不确定的。从本质上讲,Python可以保留释放的内存以供重用,即使Python解释器没有,系统分配器也可以这样做。简而言之,这可能不值得付出努力 - 请参阅我的答案的结尾以获取更多信息。

然而,这并不像看起来那么糟糕,因为Linux(以及其他现代Unix变体)使用所谓的copy-on-write语义来确保ps的行为不是这样的非常低效。本质上,子进程保留对与父进程相同的内存页面的引用 - 只要两个进程都没有进行任何更改,内存就不会实际重复,尽管如果你总结内存使用数据来自topfree对于这两个进程,它将被计算两次,因为它们的每个进程方法不够智能,无法注意页面的共享。这与拥有相同底层文件的多个硬链接没有什么不同,如果这是您遇到的事情。

一旦进程写入内存页面, 然后被复制(因此名称为“copy on write”),因此将使用实际的物理内存。在这种情况下,所需的额外内存量很难预测,因为它涉及将Python数据结构一直映射到物理内存页面。但是,原则本身就是重要的。

您可以使用-/+ buffers/cache实用程序来测试我的理论是否正确,以显示整体系统内存使用情况并比较两种情况之间的数字 - 如果我是对的,您会看到 some < / em>在100个子进程的情况下增加了内存,但没有检查每个进程的内存使用情况那么多。不要忘记使用第二行中的数字(即{{1}}行),因为这将平滑两次测试之间文件系统缓存中的任何更改。

假设这是正确的,最好的办法是在父进程分配了大量内存之前尽早尝试启动子进程。但是,除了您在此方面的最大努力之外,您可能不需要过多担心它 - 即使页面在写入时被复制,它们也不会被子进程访问,因此将被换出到磁盘必要且可能永远不会交换回来,因此不会造成很大的性能损失(除非您的平台没有任何交换)。

最后一点 - 在实践中,创建更多工作进程可能没有什么意义,而不是机器上的核心,除非你使用非常专业的硬件,否则通常不会超过8或16。如果你创建了太多的进程,那么你可能会浪费更多的时间来安排它们而不是获得好处 - 无论你做什么,你都无法获得比物理核心更多的并行化(虽然hyperthreading稍微复杂一点)。

This other SO question可能会提供更多有用的信息。