为什么在 Linux 上的 python 3 中,“多进程”版本比单进程版本花费的时间更长?
Why does a `multiprocessing` version take longer than a single process version in python 3 on Linux?
我正在尝试将一个函数应用于大量 range
个数字 - 而我使用 multiprocessing
中的池的版本比我估计的 [=] 需要更长的时间才能完成38=] 版本 -
这是我的代码有问题吗?还是Python?或者 Linux?
我使用的函数是is_solution
定义如下-
as_ten_digit_string = lambda x: f"0000000000{x}"[-10:]
def sum_of_digits(nstr):
return sum([int(_) for _ in list(nstr)])
def is_solution(x):
return sum_of_digits(as_ten_digit_string(x)) == 10
当我 运行 is_solution
处理一百万个数字时 - 大约需要 2 秒
In [13]: %timeit [is_solution(x) for x in range(1_000_000)]
1.9 s ± 18.7 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
基于此 - 对于约 100 亿个数字 - 应该需要大约 20,000 秒或大约 6 小时。但是 multiprocessing
版本即使在 9 小时后也没有结束。
我正在使用这样的多处理模块 -
from multiprocessing import Pool
with Pool(processes=24) as p:
for solution in p.imap_unordered(is_solution, range(1_000_000_000, 9_999_999_999)):
if solution:
print(solution)
我使用的 python 版本是 linux 上的 3.8
。
我不知道这是否相关 - 当我 运行 linux 中的 top
命令时 - 我看到当我的主程序有 运行 ~200 分钟 - 我的每个工作进程都有大约 20 分钟的 CPU 时间。
def solution(n, sum):
"""Generates numbers of n digits with the given total sum"""
if n == 1 and sum < 10:
yield str(sum)
return
if n < 1 or (sum > 9 and n < 2):
return
if sum == 0:
yield "0" * n
return
for digit in range(min(sum + 1, 10)):
for s in solution(n - 1, sum - digit):
yield str(digit) + s
# Print all 4-digits numbers with total sum 10
for s in solution(4, 10):
print(s)
# Print all 4-digits numbers with total sum 10, not starting with zero
for digit in range(1, 10):
for s in solution(3, 10 - digit):
print(str(digit) + s)
多重处理不是免费的。如果您有 X cpu 个核心,那么生成超过 X 个进程最终将导致性能下降。如果您的进程执行 I/O,那么即使生成 10*X 进程也可能没问题。因为他们不紧张 cpu。但是,如果您的进程进行计算和内存操作,那么 X 之上的任何进程可能只会降低性能。在评论中你说你有 4 个核心,所以你应该设置 Pool(processes=4)
。您也可以尝试不同的值。 Multiprocessing很难,可能5个甚至8个进程还是会提高性能的。但是极有可能超过 4 个 cpu 内核的 24 个进程只会损害性能。
您可以做的另一件事是将数据批量发送到子进程。目前,您一个接一个地发送数据,并且由于您的计算速度很快(对于单个数据点),因此进程间通信可能占总执行时间的大部分。这是您在单进程场景中不付出但在多进程场景中总是付出的代价。要尽量减少其影响,请使用 imap_unordered
的 chunksize
参数。
最后,尝试重新实现您的算法以避免暴力破解。正如@Alex 所建议的。
我正在尝试将一个函数应用于大量 range
个数字 - 而我使用 multiprocessing
中的池的版本比我估计的 [=] 需要更长的时间才能完成38=] 版本 -
这是我的代码有问题吗?还是Python?或者 Linux?
我使用的函数是is_solution
定义如下-
as_ten_digit_string = lambda x: f"0000000000{x}"[-10:]
def sum_of_digits(nstr):
return sum([int(_) for _ in list(nstr)])
def is_solution(x):
return sum_of_digits(as_ten_digit_string(x)) == 10
当我 运行 is_solution
处理一百万个数字时 - 大约需要 2 秒
In [13]: %timeit [is_solution(x) for x in range(1_000_000)]
1.9 s ± 18.7 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
基于此 - 对于约 100 亿个数字 - 应该需要大约 20,000 秒或大约 6 小时。但是 multiprocessing
版本即使在 9 小时后也没有结束。
我正在使用这样的多处理模块 -
from multiprocessing import Pool
with Pool(processes=24) as p:
for solution in p.imap_unordered(is_solution, range(1_000_000_000, 9_999_999_999)):
if solution:
print(solution)
我使用的 python 版本是 linux 上的 3.8
。
我不知道这是否相关 - 当我 运行 linux 中的 top
命令时 - 我看到当我的主程序有 运行 ~200 分钟 - 我的每个工作进程都有大约 20 分钟的 CPU 时间。
def solution(n, sum):
"""Generates numbers of n digits with the given total sum"""
if n == 1 and sum < 10:
yield str(sum)
return
if n < 1 or (sum > 9 and n < 2):
return
if sum == 0:
yield "0" * n
return
for digit in range(min(sum + 1, 10)):
for s in solution(n - 1, sum - digit):
yield str(digit) + s
# Print all 4-digits numbers with total sum 10
for s in solution(4, 10):
print(s)
# Print all 4-digits numbers with total sum 10, not starting with zero
for digit in range(1, 10):
for s in solution(3, 10 - digit):
print(str(digit) + s)
多重处理不是免费的。如果您有 X cpu 个核心,那么生成超过 X 个进程最终将导致性能下降。如果您的进程执行 I/O,那么即使生成 10*X 进程也可能没问题。因为他们不紧张 cpu。但是,如果您的进程进行计算和内存操作,那么 X 之上的任何进程可能只会降低性能。在评论中你说你有 4 个核心,所以你应该设置 Pool(processes=4)
。您也可以尝试不同的值。 Multiprocessing很难,可能5个甚至8个进程还是会提高性能的。但是极有可能超过 4 个 cpu 内核的 24 个进程只会损害性能。
您可以做的另一件事是将数据批量发送到子进程。目前,您一个接一个地发送数据,并且由于您的计算速度很快(对于单个数据点),因此进程间通信可能占总执行时间的大部分。这是您在单进程场景中不付出但在多进程场景中总是付出的代价。要尽量减少其影响,请使用 imap_unordered
的 chunksize
参数。
最后,尝试重新实现您的算法以避免暴力破解。正如@Alex 所建议的。