我正在尝试在Python中通过多处理来加速一些代码,但我无法理解一点。假设我有以下哑函数:
import time
from multiprocessing.pool import Pool
def foo(_):
for _ in range(100000000):
a = 3
当我在笔记本电脑(英特尔 - 8 核 CPU(上不使用多处理(请参阅下面的代码(运行此代码时,花费的时间是 ~2.31 秒。
t1 = time.time()
foo(1)
print(f"Without multiprocessing {time.time() - t1}")
相反,当我使用 Python 多处理库运行此代码时(请参阅下面的代码(,所花费的时间是 ~6.0 秒。
pool = Pool(8)
t1 = time.time()
pool.map(foo, range(8))
print(f"Sample multiprocessing {time.time() - t1}")
据我所知,我知道在使用多处理时,有一些时间开销主要是由于需要生成新进程和复制内存状态。但是,当处理的最初生成时,此操作应该只执行一次,并且不应该那么大。
那么我在这里缺少什么?我的推理有问题吗?
编辑:我认为最好更明确地回答我的问题。我在这里期望的是多处理代码比顺序代码略慢。的确,我不会将整个工作拆分到 8 个内核,但我并行使用 8 个内核来完成相同的工作(因此在理想情况下,处理时间应该或多或少保持不变(。考虑到生成新进程的开销,我预计总时间会增加一些(不是太大(百分比,但当我到达这里时不会增加~2.60倍。
好吧,多处理不可能使这更快:你不是在 8 个进程中划分工作,而是要求 8 个进程中的每一个来做整个事情。 每个进程至少需要与代码在不使用多处理的情况下只执行一次的时间一样长。
因此,如果多处理根本没有帮助,您预计它花费的时间大约是单处理器运行的 8 倍(它完成的工作量是单处理器运行的 8 倍!(。 但是你说它不需要 2.31 * 8 ~= 18.5 秒,而是"只需要"大约 6 秒。 因此,您的速度比 3 倍加速要好。
为什么不不止于此呢? 无法从这里猜测。 这将取决于你的机器有多少物理内核,以及你同时运行多少其他东西。 对于此特定功能,每个进程都将 100% 受 CPU 限制,因此"逻辑"内核的数量几乎无关紧要 - 处理器超线程几乎没有机会提供帮助。 所以我猜你有 4 个物理核心。
在我的盒子上
我的盒子上的示例计时,它有 8 个逻辑内核,但只有 4 个物理内核,否则盒子非常安静:
Without multiprocessing 2.468580484390259
Sample multiprocessing 4.78624415397644
如上所述,这些都没有让我感到惊讶。 事实上,我对程序如何有效地用尽机器的真实容量感到有点惊讶(但令人愉快(。
@TimPeters已经回答说,您实际上只是在 8 个池子进程中运行作业 8 次,因此它更慢而不是更快。
这回答了问题,但并没有真正回答你真正的潜在问题是什么。从您对这个结果的惊讶中可以清楚地看出,您期望单个作业以某种方式自动拆分并在 8 个池进程中分段运行。这不是它的工作方式。你必须内置/告诉它如何拆分工作。
不同类型的作业需求需要以不同的方式细分,但要继续您的示例,您可以执行以下操作:
import time
from multiprocessing.pool import Pool
def foo(_):
for _ in range(100000000):
a = 3
def foo2(job_desc):
start, stop = job_desc
print(f"{start}, {stop}")
for _ in range(start, stop):
a = 3
def main():
t1 = time.time()
foo(1)
print(f"Without multiprocessing {time.time() - t1}")
pool_size = 8
pool = Pool(pool_size)
t1 = time.time()
top_num = 100000000
size = top_num // pool_size
job_desc_list = [[size * j, size * (j+1)] for j in range(pool_size)]
# this is in case the the upper bound is not a multiple of pool_size
job_desc_list[-1][-1] = top_num
pool.map(foo2, job_desc_list)
print(f"Sample multiprocessing {time.time() - t1}")
if __name__ == "__main__":
main()
这导致:
Without multiprocessing 3.080709171295166
0, 12500000
12500000, 25000000
25000000, 37500000
37500000, 50000000
50000000, 62500000
62500000, 75000000
75000000, 87500000
87500000, 100000000
Sample multiprocessing 1.5312283039093018
如图所示,拆分作业确实可以减少时间。加速将取决于 CPU 的数量。在 CPU 密集型作业中,应尝试将其池大小限制为 CPU 数。我的笔记本电脑有更多的CPU,但一些好处被开销所取代。如果作业更长,这应该看起来更有用。