Я разработал простую программу для решения проблемы восьми королев. Теперь я хотел бы провести еще несколько тестов с различными мета-параметрами, поэтому я хотел бы сделать это быстро. Я прошел несколько итераций профилирования и смог значительно сократить время выполнения, но я достиг точки, когда я считаю, что только части вычислений одновременно могут сделать это быстрее. Я пытался использовать модули 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, 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 с Python 3.6.4 |Anaconda, Inc.|
. Я читал здесь о SO ( Почему создание нового процесса в Windows дороже, чем Linux? ), что создание новых процессов в Windows дорого. Есть ли что-нибудь, что можно сделать, чтобы ускорить процесс? Я что-то упускаю из виду?
Я также пытался создать Pool
только один раз, но это не очень помогло.
Edit:
Исходная структура кода выглядит примерно так:
Мой код более или менее похож на структуру:
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
элементов. Невозможно заранее определить, насколько быстро сходятся алгоритмы и сколько итераций выполняется, возможно, тысячи.