使用Python在Windows上实现并发/并行
我开发了一个简单的程序来解决八皇后问题。现在我想用不同的元参数做更多的测试,所以我想让它更快。我进行了几次分析迭代,能够显著缩短运行时间,但我认为只有部分计算同时进行才能加快速度。我尝试使用使用Python在Windows上实现并发/并行,python,windows,multiprocessing,Python,Windows,Multiprocessing,我开发了一个简单的程序来解决八皇后问题。现在我想用不同的元参数做更多的测试,所以我想让它更快。我进行了几次分析迭代,能够显著缩短运行时间,但我认为只有部分计算同时进行才能加快速度。我尝试使用多处理和并发.futures模块,但它并没有显著提高运行时,在某些情况下甚至会降低执行速度。这只是提供一些背景 我能够提出类似的代码结构,其中顺序版本优于并发版本 import numpy as np import concurrent.futures import math import time impo
多处理
和并发.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、Intel Core 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上使用python3.6.4 | Anaconda,Inc.
同时运行它,我是否能够以某种方式使它更快。我在这里读到,在Windows上创建新进程是昂贵的。有什么办法可以加快速度吗?我错过了什么明显的东西吗
我还试着只创建一次池
,但似乎没有多大帮助
编辑: 原始代码结构大致如下所示: 我的代码的结构大致如下:
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
元素。无法提前告知算法收敛的速度和执行的迭代次数,可能是数千次。在UNIX变体下,进程要轻得多。Windows进程很繁重,启动时间要长得多。线程是在windows上执行多处理的推荐方法。
您也可以遵循此线程:
您的设置对多处理不太公平。你甚至包括了不必要的
prime=None
赋值
有几点:
数据大小 您生成的数据是一种很小的方法,可以让流程创建的开销得到回报。尝试使用
范围(1\u 000\u 000)
而不是范围(5000)
。在具有多处理.start\u方法的Linux上
设置为“spawn”(Windows上的默认设置),这将绘制一幅不同的画面:
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块留在池中。如果一开始只创建一次池,那么将池创建包含到基准测试中就没有多大意义
Numpy
Numpy部分能够释放全局解释器锁()。这意味着,您可以从多核并行性中获益,而无需增加进程创建的开销。如果你在做数学,尽量利用numpy。尝试
concurrent.futures.ThreadPoolExecutor
和multi-processing.dummy.Pool
,代码使用numpy。使用numba或Cython。你问题中的链接与OP问题中的链接相同……因此,我认为这没有多大帮助,由于GIL和CPU的限制,线程对IO绑定的任务很有帮助。是的,确实如此,线程比进程轻。尝试一下,你会看到不同之处。感谢您花时间研究这个问题,并在Linux上运行代码。我目前没有访问任何Linux环境的权限,但我很想看看那里的数字如何。我之所以使用包含5000个元素的数组,是因为它更好地反映了原始代码中发生的情况,同时也显示了运行时的差异。我修改了这个问题来描述它。还有一点,我正在重用池
,以使代码保持可读性。多亏了Numpy,我能够显著提高代码其他部分的性能,因此我非常欣赏它提供的性能提升,但我认为我不能将其用于其余部分。在这一点上,我很想知道Windows上多处理的局限性是什么。可能是因为对于包含1000
元素的列表,创建新流程毫无意义,因为开销太大了吗?@Grzegorz这与列表大小无关,而是与按顺序处理它所需的时间有关。如果这只需要几毫秒,开销就无法支付。不仅创建一个新流程,而且仅仅将工作发送到现有池中都要付出代价,因为已经对数据进行酸洗和取消酸洗需要一些时间。您必须权衡串行计算持续时间与mp开销,看看是否值得。@Grzegorz仅供参考:在具有分叉的Linux上,我得到的范围(5000),473个素数:并发:0.013890/顺序:0.008119/多处理:0.105427,范围(1_000_000),89479个素数:并发:0.97016/顺序:1.263134/多处理:0.611853