我正在尝试通过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倍。
答案 0 :(得分:9)
嗯,多重处理不可能使速度更快:您没有将工作划分为8个流程,而是要求8个流程中的每一个都完成整个工作。不使用多处理,每个过程至少需要花费您的代码一次的时间。
因此,如果多处理根本无济于事,那么您希望它花费的时间大约是单处理器运行时间的8倍(工作量的8倍!)。但是您说它不需要花费2.31 * 8〜= 18.5秒,而是“仅”大约6秒。因此,您获得的提升比3倍加速要好。
为什么不超过?从这里无法猜测。这将取决于您的计算机有多少个物理核心,以及您同时运行多少其他东西。对于该特定功能,每个进程都将100%受CPU限制,因此“逻辑”内核的数量几乎无关紧要-处理器超线程帮助的机会很少。所以我猜您有4个物理核心。
我的机器上有8个逻辑核心,但只有4个物理核心的示例计时,否则让盒子显得很安静:
Without multiprocessing 2.468580484390259
Sample multiprocessing 4.78624415397644
如上所述,这些都没有让我感到惊讶。实际上,我 对该程序用尽了机器的真实容量的效率感到有些惊讶(但令人愉快)。
答案 1 :(得分:1)
@TimPeters已经回答您实际上只是在8个Pool子进程中运行了8次作业,所以它变慢而不是变快。
这可以回答问题,但不能真正回答您真正的内在问题。令您惊讶的是,您很清楚地看到,您期望将单个作业以某种方式自动拆分并在8个Pool流程中分批运行。那不是它的工作方式。您必须内置/告诉它如何分散工作。
需要以不同的方式细分不同种类的工作,但是为了继续进行示例,您可以执行以下操作:
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,但是部分好处却被开销所抵消。如果工作时间更长,这应该看起来更有用。