Несколько слов о multiprocessing Pool
дата публикации: 2016-02-12

В первое время python был для меня языком для решения математических задач, построения алгоритмов и т.д. На тот момент я не задумывался о применении этого языка в задачах web программирования и спокойно писал на php, вообще не задумывался о "фишках", которые позволяют использовать преимущества конкретного языка. Однако рано или поздно каждый программист мыслит о построении кода, который не просто работает, а работает оптимально по тем или иным критериям. Если говорить о времени работы программы, то тут обязательно нужно знать подходы к распараллеливанию кода.

Вообще, python в этом отношении имеет ряд проблем, которые связаны по большей степени с Global Interpreter Lock (GIL), работа которого заложена в идеологию python. Не хочу вдаваться в подробности вопроса надобности такого замка, не буду рассказывать обходные пути, а просто покажу пример оптимизации работы программы по критерию быстродействия с помощью модуля multiprocessing

# подключение библиотек
from multiprocessing import Pool, cpu_count
import time
# описание функции, выполняющей роль нагрузки
def loadFunction(n) :
    i = 0
    while i < n :
        i += 1
#начало программы
if __name__ == '__main__' :
    k = 20
    n = 10000000
    s = 1
    clc = time.time()
    if s == 0 :
        for i in range(k) :
            loadFunction(n)
    elif s == 1 :
        pool = Pool(cpu_count())
        arg = []
        for i in range(k) :
            arg.append(n)
        pool.map(loadFunction, arg)
    clc = time.time() - clc
    print('время {0:.2f} секунд'.format(clc))
    input()

В представленном вашему вниманию коде реализовано два варианта развития событий, первый - расчет функции нагрузки в одном процессе, во втором случае число процессов равно количеству ядер процессора, в моем случае 2 ядра. Полученные результаты работы программы: без использования мультипроцессов (s = 0) 20.97 секунд на работу программы, с использованием мультипроцессов (s = 1) 16.31, что более чем на 20% быстрее. Приведу графики загрузки процессора и количества процессов

График

Первый график показывает, что программа не загружала второе ядро, а только одно и то не на 100%, в памяти "висел" один процесс (второй это IDLE), а вот во втором случае загрузка обеих ядер была 100% при этом было запущено три процесса (один родительский и два дочерних) в памяти.

В качестве вывода хочу сказать, что распараллеливание следует применять там, где оно действительно необходимо, так как дополнительный процесс в памяти как минимум требует время на запуск. В этой короткой статье не рассматриваю вопрос создания потоков, ввиду того, что без "костылей" не имеет смысла реализовывать их в python.