我开发了简单的程序来解决八个皇后的问题。现在我想用不同的元参数做更多的测试,所以我想让它更快。我经历了几次分析迭代,能够显着缩短运行时间,但我相信只有部分并发计算才能使其更快。我尝试使用multiprocessing
和concurrent.futures
模块,但它并没有大幅提高运行时,在某些情况下甚至会减慢执行速度。那只是为了提供一些背景。
我能够提出类似的代码结构,其中顺序版本胜过并发。
import numpy as np
import concurrent.futures
import math
import time
import multiprocessing
def is_prime(n):
if n % 2 == 0:
return False
sqrt_n = int(math.floor(math.sqrt(n)))
for i in range(3, sqrt_n + 1, 2):
if n % i == 0:
return False
return True
def generate_data(seed):
np.random.seed(seed)
numbers = []
for _ in range(5000):
nbr = np.random.randint(50000, 100000)
numbers.append(nbr)
return numbers
def run_test_concurrent(numbers):
print("Concurrent test")
start_tm = time.time()
chunk = len(numbers)//3
primes = None
with concurrent.futures.ProcessPoolExecutor(max_workers=3) as pool:
primes = list(pool.map(is_prime, numbers, chunksize=chunk))
print("Time: {:.6f}".format(time.time() - start_tm))
print("Number of primes: {}n".format(np.sum(primes)))
def run_test_sequential(numbers):
print("Sequential test")
start_tm = time.time()
primes = [is_prime(nbr) for nbr in numbers]
print("Time: {:.6f}".format(time.time() - start_tm))
print("Number of primes: {}n".format(np.sum(primes)))
def run_test_multiprocessing(numbers):
print("Multiprocessing test")
start_tm = time.time()
chunk = len(numbers)//3
primes = None
with multiprocessing.Pool(processes=3) as pool:
primes = list(pool.map(is_prime, numbers, chunksize=chunk))
print("Time: {:.6f}".format(time.time() - start_tm))
print("Number of primes: {}n".format(np.sum(primes)))
def main():
nbr_trails = 5
for trail in range(nbr_trails):
numbers = generate_data(trail*10)
run_test_concurrent(numbers)
run_test_sequential(numbers)
run_test_multiprocessing(numbers)
print("--n")
if __name__ == '__main__':
main()
当我在我的机器上运行它时 - Windows 7,具有四个内核的英特尔酷睿i5,我得到了以下输出:
Concurrent test
Time: 2.006006
Number of primes: 431
Sequential test
Time: 0.010000
Number of primes: 431
Multiprocessing test
Time: 1.412003
Number of primes: 431
--
Concurrent test
Time: 1.302003
Number of primes: 447
Sequential test
Time: 0.010000
Number of primes: 447
Multiprocessing test
Time: 1.252003
Number of primes: 447
--
Concurrent test
Time: 1.280002
Number of primes: 446
Sequential test
Time: 0.010000
Number of primes: 446
Multiprocessing test
Time: 1.250002
Number of primes: 446
--
Concurrent test
Time: 1.260002
Number of primes: 446
Sequential test
Time: 0.010000
Number of primes: 446
Multiprocessing test
Time: 1.250002
Number of primes: 446
--
Concurrent test
Time: 1.282003
Number of primes: 473
Sequential test
Time: 0.010000
Number of primes: 473
Multiprocessing test
Time: 1.260002
Number of primes: 473
--
我遇到的问题是,我是否可以通过在Windows上同时运行它来使其更快Python 3.6.4 |Anaconda, Inc.|
.我在SO上读到(为什么在Windows上创建新进程比Linux更昂贵?(在Windows上创建新进程是昂贵的。有什么可以加快速度的吗?我错过了一些明显的东西吗?
我也尝试只创建一次Pool
,但它似乎没有多大帮助。
编辑:
原始代码结构看起来或多或少像:
我的代码结构或多或少是这样的:
class Foo(object):
def g() -> int:
# function performing simple calculations
# single function call is fast (~500 ms)
pass
def run(self):
nbr_processes = multiprocessing.cpu_count() - 1
with multiprocessing.Pool(processes=nbr_processes) as pool:
foos = get_initial_foos()
solution_found = False
while not solution_found:
# one iteration
chunk = len(foos)//nbr_processes
vals = list(pool.map(Foo.g, foos, chunksize=chunk))
foos = modify_foos()
foos
有1000
元素。不可能提前知道算法收敛的速度有多快,以及执行了多少次迭代,可能是数千次。
您的设置对多处理并不公平。 您甚至包括了不必要的primes = None
作业。;)
几点:
数据大小
您生成的数据可以重新获得流程创建的开销。尝试使用range(1_000_000)
而不是range(5000)
.在multiprocessing.start_method
设置为"spawn"(Windows上默认(的Linux上,这绘制了不同的画面:
Concurrent test
Time: 0.957883
Number of primes: 89479
Sequential test
Time: 1.235785
Number of primes: 89479
Multiprocessing test
Time: 0.714775
Number of primes: 89479
重复使用您的池
只要您在程序中留下了以后要并行化的任何代码,就不要离开池的with-block。如果您在开始时只创建一次池,那么将池创建纳入基准测试根本没有多大意义。
努比
Numpy 部分能够释放全局解释器锁 (GIL(。这意味着,您可以从多核并行性中受益,而无需产生进程创建的开销。如果你无论如何都在做数学,试着尽可能多地使用numpy。尝试使用 numpyconcurrent.futures.ThreadPoolExecutor
和multiprocessing.dummy.Pool
代码。
在 UNIX 变体下,进程要轻量级得多。Windows 进程很繁重,需要更多时间才能启动。线程是在窗口上进行多处理的推荐方法。 您也可以关注此线程: 为什么在Windows上创建新进程比Linux更昂贵?