Пробую multiprocessing в Python 3
Недавно потребовалось немного познакомиться с модулем multiprocessing
, в этом посте немного опишу свой опыт, попробую начать с базового примера, который дается в документации. Использовать буду, конечно, Python 3.
Итак, базовый пример из документации выглядит так:
1 from multiprocessing import Process 2 3 def f(name): 4 print('hello', name) 5 6 if __name__ == '__main__': 7 p = Process(target=f, args=('bob',)) 8 p.start() 9 p.join()
Запустим:
hello bob
Попробую создать несколько процессов:
1 from multiprocessing import Process 2 3 4 def f(name): 5 print('hello', name) 6 7 8 if __name__ == '__main__': 9 for name in ('alex', 'test', 'bob'): 10 p = Process(target=f, args=(name,)) 11 p.start() 12 p.join()
После запуска:
hello alex hello test hello bob
Хорошо, но что если, я хочу запустить несколько процессов и затем сформировать один массив из данных? В этом может помочь такая штука как Queue
, примерно так:
1 from multiprocessing import Process, Queue 2 3 4 def f(name, q): 5 for i in range(3): 6 q.put(('hello_{0}'.format(i), name)) 7 8 9 if __name__ == '__main__': 10 result = [] 11 for name in ('alex', 'test', 'bob'): 12 q = Queue() 13 p = Process(target=f, args=(name, q)) 14 p.start() 15 p.join() 16 for _ in range(q.qsize()): 17 result.append(q.get()) 18 print(result)
Этот код выведет:
[ ('hello_0', 'alex'), ('hello_1', 'alex'), ('hello_2', 'alex'), ('hello_0', 'test'), ('hello_1', 'test'), ('hello_2', 'test'), ('hello_0', 'bob'), ('hello_1', 'bob'), ('hello_2', 'bob') ]
Хорошо, но теперь есть желание посмотреть как сильно использование multiprocessing
может ускорить какую-либо задачу, хотя бы конкретно на моей машине. Я решил сделать такой тест, используя модуль timeit
, так:
1 from multiprocessing import Process, Queue 2 import timeit 3 4 5 def f(name, q): 6 for i in range(1000): 7 q.put(('hello_{0}'.format(i), name)) 8 9 10 def multiprocessing_test(names): 11 """Тест функции cо многими процессами.""" 12 result = [] 13 for name in names: 14 q = Queue() 15 p = Process(target=f, args=(name, q)) 16 p.start() 17 p.join() 18 for _ in range(q.qsize()): 19 result.append(q.get()) 20 21 22 def without_multi_test(names): 23 """Тест обычной функции.""" 24 result = [] 25 for name in names: 26 q = Queue() 27 f(name, q) 28 for _ in range(q.qsize()): 29 result.append(q.get()) 30 31 32 if __name__ == '__main__': 33 names = ('alex', 'test', 'bob', 'lol1', 'lol2', 'lol3') 34 print(timeit.timeit(lambda: multiprocessing_test(names), number=10)) 35 print(timeit.timeit(lambda: without_multi_test(names), number=10))
Получилось, что для этой задачи, на таких объемах данных, применение multiprocessing
оправдано:
1.7380545689957216 2.3318959810130764
Интересно, что если я снижал количество итераций в функции f
до 100
, то обычный код начинал работать быстрее, вообще, получилось интересно.