我有课:
import numpy as np
from multiprocessing import Pool
class example
def __init__(self):
self.arr = np.random.rand(1000, 1000, 1000, 1000)
def f(self, a):
return self.arr[0, 1, 2, 3] * a
s = example()
if __name__ == "__main__":
with Pool(8) as po:
x = po.map(s.f, range(100))
因为我的
arr
中有一个巨大的 numpy 数组 class example
。我有一个称为 arr
的方法来进行一些计算。然后我初始化该类并调用该方法 f
100 次。在这种情况下,
arr
加上一些小但可以忽略不计的s.f
100 次之后的一些小问题?我在高性能计算设施中运行了类似的问题,但发现内存使用量比
arr
大得多。不知道为什么。
我坚信1是正确的。但最终结果更喜欢2?
保存其他可能会改变程序运行方式的框架(例如,将检测您的代码的第 3 方日志库),此代码的行为如下:
对于多处理池中的每个进程,它将创建其所有数据。因此,如果池为“8”且映射如下,则需要 8 倍的
self.arr
大小 - 如果您不在代码中创建新的 example
类实例:池中的每个进程都会有一个 example
实例。除此之外,每次您调用 f
时,它都会计算一个新数组,其大小与计算中所需的大小相同。
但是,如果任何代码calls“f”以不跨调用“回收”的方式存储其结果,这些中间值将保留在程序中 - 在您的情况下,它们可能会在每个工作进程中累积在您创建的多处理池中。
由于 multiprocessing.map 代码将尽快运行任务,因此当您从
x
获取一个结果时,当您在主代码中处理它时,后台进程将生成新项目并将其放入排队以便他们准备好。换句话说:根据处理 x
的每一项所需的时间(并且 100% 确定是否将 x
转换为 list
),是的,您可能会获得 100 个 f
输出的副本,在瞬态时刻,甚至更多:当使用多处理时,来回传递数据会产生开销 - 人们可能会说数据的原始副本、“传输中”副本和目标副本将存在:所以一次将存在 self.f
返回值的三倍。 (但是你不会得到 f
返回值的“300”倍。
可以更改代码,以便在完成上一次迭代的值后,只需向多处理工作线程提交新任务,而不是使用 Poll.map。您应该更改代码以直接使用
concurrent.futures.ProcessPoolExecutor
而不是 multiprocessing.Poll
,并在完成 submit
调用的先前结果后调用 example.f
进行下一次迭代 example.f
。
https://docs.python.org/3/library/concurrent.futures.html