使用python的multiprocessing
模块,以下设计的示例以最小的内存需求运行:
import multiprocessing
# completely_unrelated_array = range(2**25)
def foo(x):
for x in xrange(2**28):pass
print x**2
P = multiprocessing.Pool()
for x in range(8):
multiprocessing.Process(target=foo, args=(x,)).start()
取消注释completely_unrelated_array
的创建,您会发现每个衍生进程都为completely_unrelated_array
的副本分配内存!这是一个更大的项目的最小例子,我无法弄清楚如何解决方法;多处理似乎可以复制全局的所有内容。我不需要一个共享内存对象,我只需要传入x
,然后处理而不用整个程序的内存开销。
侧面观察:有趣的是print id(completely_unrelated_array)
内的foo
给出了相同的值,暗示某些可能不是副本......
答案 0 :(得分:9)
由于os.fork()
的性质,__main__
模块的全局命名空间中的任何变量都将由子进程继承(假设你在Posix平台上),所以你和&#39} #39; ll看到孩子们的内存使用情况会在他们创建后立即反映出来。我不确定是否真的分配了所有内存,据我所知,内存是共享的,直到你真正尝试在子代中更改内存,此时会创建一个新副本。另一方面,Windows不使用os.fork()
- 它会重新导入每个子节点中的主模块,并挑选您想要发送给子节点的任何局部变量。因此,使用Windows,您实际上可以通过仅在if __name__ == "__main__":
防护中定义它来避免在子级中复制的大型全局结尾,因为该防护内部的所有内容都只会在父进程中运行:
import time
import multiprocessing
def foo(x):
for x in range(2**28):pass
print(x**2)
if __name__ == "__main__":
completely_unrelated_array = list(range(2**25)) # This will only be defined in the parent on Windows
P = multiprocessing.Pool()
for x in range(8):
multiprocessing.Process(target=foo, args=(x,)).start()
现在,在Python 2.x中,如果您正在使用Posix平台,则只能通过分叉来创建新的multiprocessing.Process
对象。但是在Python 3.4上,您可以使用上下文指定如何创建新进程。因此,我们可以指定"spawn"
上下文,这是Windows使用的上下文,以创建我们的新进程,并使用相同的技巧:
# Note that this is Python 3.4+ only
import time
import multiprocessing
def foo(x):
for x in range(2**28):pass
print(x**2)
if __name__ == "__main__":
completely_unrelated_array = list(range(2**23)) # Again, this only exists in the parent
ctx = multiprocessing.get_context("spawn") # Use process spawning instead of fork
P = ctx.Pool()
for x in range(8):
ctx.Process(target=foo, args=(x,)).start()
如果您需要2.x支持,或者希望坚持使用os.fork()
来创建新的Process
对象,我认为您可以做的最好的事情是将报告的内存使用率降低,立即删除儿童的违规物品:
import time
import multiprocessing
import gc
def foo(x):
init()
for x in range(2**28):pass
print(x**2)
def init():
global completely_unrelated_array
completely_unrelated_array = None
del completely_unrelated_array
gc.collect()
if __name__ == "__main__":
completely_unrelated_array = list(range(2**23))
P = multiprocessing.Pool(initializer=init)
for x in range(8):
multiprocessing.Process(target=foo, args=(x,)).start()
time.sleep(100)
答案 1 :(得分:4)
这里重要的是您要定位的平台。 Unix系统进程是使用Copy-On-Write(cow)内存创建的。因此,即使每个进程都获得父进程的完整内存的副本,该内存实际上仅在修改时按页面基数(4KiB)分配。 因此,如果您只是针对这些平台,则无需进行任何更改。
如果您的目标是没有牛叉的平台,您可能需要使用python 3.4及其新的分叉上下文spawn
和forkserver
,请参阅documentation
这些方法将创建与父进程无共享或有限状态的新进程,并且所有内存传递都是显式的。
但并非产生的进程将导入您的模块,因此将显式复制所有全局数据,并且不可能进行写入时复制。为防止这种情况,您必须缩小数据范围。
import multiprocessing as mp
import numpy as np
def foo(x):
import time
time.sleep(60)
if __name__ == "__main__":
mp.set_start_method('spawn')
# not global so forks will not have this allocated due to the spawn method
# if the method would be fork the children would still have this memory allocated
# but it could be copy-on-write
completely_unrelated_array = np.ones((5000, 10000))
P = mp.Pool()
for x in range(3):
mp.Process(target=foo, args=(x,)).start()
例如,spawn的顶部输出:
%MEM TIME+ COMMAND
29.2 0:00.52 python3
0.5 0:00.00 python3
0.5 0:00.00 python3
0.5 0:00.00 python3
并使用fork:
%MEM TIME+ COMMAND
29.2 0:00.52 python3
29.1 0:00.00 python3
29.1 0:00.00 python3
29.1 0:00.00 python3
请注意,由于写时复制
,其超过100%