这个问题经常被问到,我找到的关于它的最佳线程(0,1(给出了答案,"这是因为工作负载不够重,所以进程间通信(IPC(开销最终会让事情变慢。
我不知道这是否正确,但是我已经隔离了一个示例,其中这种情况在相同的工作负载中发生并且不会发生,我想知道这个答案是否仍然适用或为什么它实际上会发生:
from multiprocessing import Pool
def f(n):
res = 0
for i in range(n):
res += i**2
return res
def single(n):
""" Single core """
for i in range(n):
f(n)
def multi(n):
""" Multi core """
pool = Pool(2)
for i in range(n):
pool.apply_async(f, (n,))
pool.close()
pool.join()
def single_r(n):
""" Single core, returns """
res = 0
for i in range(n):
res = f(n) % 1000 # Prevent overflow
return res
def multi_r(n):
""" Multi core, returns """
pool = Pool(2)
res = 0
for i in range(n):
res = pool.apply_async(f, (n,)).get() % 1000
pool.close()
pool.join()
return res
# Run
n = 5000
if __name__ == "__main__":
print(f"single({n})...", end='')
single(n)
print(" DONE")
print(f"multi({n})...", end='')
multi(n)
print(" DONE")
print(f"single_r({n})...", end='')
single_r(n)
print(" DONE")
print(f"multi_r({n})...", end='')
multi_r(n)
print(" DONE")
工作负载f()
。
f()
以单核和双核运行,无需通过single()
和multi()
进行回访。
然后f()
运行单核和双核,通过single_r()
和multi_r()
进行回访。
我的结果是,当f()
通过回声多处理运行时,速度会变慢。没有回报,它就不会发生。
所以single()
需要q
秒。 multi()
要快得多。好。然后single_r()
需要 q
秒。但是multi_r()
需要的不仅仅是q
秒。对我的系统监视器的目视检查证实了这一点(有点难以分辨,但驼峰multi(n)
有两种颜色,表明来自两个不同内核的活动(。
此外,终端输出的佐证视频
即使工作负载统一,这仍然是 IPC 开销吗?是否仅在其他进程返回其结果时才支付此类开销,如果是,是否有办法在返回结果的同时避免它?
正如 Darkonaut 指出的那样,在 multi_r()
中使用多个进程时速度变慢是因为get()
调用阻塞:
for i in range(n):
res = pool.apply_async(f, (n,)).get() % 1000
这有效地按顺序或并发运行工作负载(更类似于多线程(,同时增加多进程开销,使其运行速度比单核等效single_r()
慢!
同时,multi()
运行得更快(即正确并行运行(,因为它不包含get()
调用。
要并行运行并返回结果,请先收集结果对象,如下所示:
def multi_r_collected(n):
""" Multi core, collects apply_async() results before returning them """
pool = Pool(2)
res = 0
res = [pool.apply_async(f, (n,)) for i in range(n)] # Collect first!
pool.close()
pool.join()
res = [r.get() % 1000 for r in res] # .get() after!
return res
对CPU活动的目视检查证实了注意到的加速;当通过Pool(12)
运行12个进程时,有一个干净,均匀的多个内核台面明显以100%并行运行(而不是multi_r(n)
的50%大杂烩(。