Использование пула для многопроцессорной обработки в Python (Windows) - PullRequest
0 голосов
/ 05 марта 2019

Я должен учиться параллельно, чтобы выполнять его намного быстрее.Я новичок в многопроцессорной библиотеке в Python, и пока не могу заставить ее работать успешно.Здесь я исследую, остается ли каждая пара (источник, цель) в определенных местах между различными рамками моего исследования.Несколько моментов:

  1. Это одна функция, которую я хочу запустить быстрее (это не несколько процессов).
  2. Процесс выполняется впоследствии;это означает, что каждый кадр сравнивается с предыдущим.
  3. Этот код является очень простой формой исходного кода.Код выводит список residece_list.
  4. Я использую ОС Windows.

Может кто-нибудь проверить код (раздел многопроцессорной обработки) и помочь мне улучшить его, чтобы он работал.Спасибо.

import numpy as np
from multiprocessing import Pool, freeze_support


def Main_Residence(total_frames, origin_list, target_list):
    Previous_List = {}
    residence_list = []

    for frame in range(total_frames):     #Each frame

        Current_List = {}               #Dict of pair and their residence for frames
        for origin in range(origin_list):

            for target in range(target_list):
                Pair = (origin, target)         #Eahc pair

                if Pair in Current_List.keys():     #If already considered, continue
                    continue
                else:
                    if origin == target:
                        if (Pair in Previous_List.keys()):            #If remained from the previous frame, add residence
                            print "Origin_Target remained: ", Pair
                            Current_List[Pair] = (Previous_List[Pair] + 1)
                        else:                                           #If new, add it to the current
                            Current_List[Pair] = 1

        for pair in Previous_List.keys():                        #Add those that exited from residence to the list
            if pair not in Current_List.keys():
                residence_list.append(Previous_List[pair])

        Previous_List = Current_List
    return residence_list

if __name__ == '__main__':
    pool = Pool(processes=5)
    Residence_List = pool.apply_async(Main_Residence, args=(20, 50, 50))
    print Residence_List.get(timeout=1)
    pool.close()
    pool.join()
    freeze_support()

Residence_List = np.array(Residence_List) * 5

1 Ответ

0 голосов
/ 05 марта 2019

Многопроцессорная обработка не имеет смысла в контексте, который вы здесь представляете.Вы создаете пять подпроцессов (и три потока, принадлежащих пулу, управляющих рабочими, задачами и результатами) для выполнения одна функция один раз .Все это обходится дорого, как в системных ресурсах, так и во времени выполнения, в то время как четыре ваших рабочих процесса вообще ничего не делают.Многопроцессорная обработка не ускоряет выполнение функции.Код в вашем конкретном примере всегда будет медленнее, чем просто выполнение Main_Residence(20, 50, 50) в основном процессе.

Чтобы многопроцессорная обработка имела смысл в таком контексте, ваша работа под рукой должна быть разбитак набору однородных задач, которые могут быть обработаны параллельно с их результатами, которые впоследствии могут быть объединены.

В качестве примера (не обязательно хорошего), если вы хотите вычислить наибольшие простые факторы для последовательностичисла, вы можете делегировать задачу расчета этого фактора для любого конкретного числа работнику в пуле.Затем несколько рабочих будут выполнять эти отдельные вычисления параллельно:

def largest_prime_factor(n):
    p = n
    i = 2
    while i * i <= n:
        if n % i:
            i += 1
        else:
            n //= i
    return p, n


if __name__ == '__main__':
    pool = Pool(processes=3)
    start = datetime.now()
    # this delegates half a million individual tasks to the pool, i.e. 
    # largest_prime_factor(0), largest_prime_factor(1), ..., largest_prime_factor(499999)      
    pool.map(largest_prime_factor, range(500000))
    pool.close()
    pool.join()
    print "pool elapsed", datetime.now() - start
    start = datetime.now()
    # same work just in the main process
    [largest_prime_factor(i) for i in range(500000)]
    print "single elapsed", datetime.now() - start

Вывод:

pool elapsed 0:00:04.664000
single elapsed 0:00:08.939000

(функция largest_prime_factor взята из @ Stefan in этот ответ )

Как видите, пул всего лишь примерно в два раза быстрее, чем выполнение одного процесса с одинаковым объемом работы, при одновременном выполнении трех процессов параллельно.Это связано с накладными расходами, вызванными многопроцессорностью / пулом.

Итак, вы заявили, что код в вашем примере был упрощен.Вам придется проанализировать исходный код, чтобы выяснить, можно ли его разбить на однородные задачи, которые можно передать в пул для обработки.Если это возможно, использование многопроцессорной обработки может помочь вам ускорить вашу программу.В противном случае многопроцессорная обработка, скорее всего, будет стоить вам времени, а не сэкономит.

Редактировать:
Поскольку вы запросили предложения по коду.Я едва могу сказать что-нибудь о твоей функции.Вы сами сказали, что это просто упрощенный пример предоставления MCVE (кстати, это высоко ценится! Большинство людей не тратят время, чтобы сократить свой код до минимума).Запросы на проверку кода в любом случае лучше подходят для Codereview .

Немного поэкспериментируйте с доступными методами делегирования задач.В моем главном примере фактора использование apply_async сопровождалось массивным штрафомВремя выполнения увеличилось в девять раз по сравнению с использованием map.Но мой пример использует простую итерацию, для вашей задачи требуется три аргумента.Это может иметь место для starmap, но это доступно только в Python 3.3.
В любом случае, структура / характер данных вашей задачи в основном определяет правильный метод для использования.
Я провел некоторое тестирование вопросов и ответов смногопроцессорность вашего примера функции.Входные данные были определены следующим образом:

inp = [(20, 50, 50)] * 5000  # that makes 5000 tasks against your Main_Residence

Я запустил это в Python 3.6 в трех подпроцессах с неизменной вашей функцией, за исключением удаления print статута (ввод-вывод является дорогостоящим).Я использовал starmap, apply, starmap_async и apply_async, а также просматривал результаты каждый раздля учета блокировки get() в асинхронных результатах.
Вот вывод:

starmap elapsed 0:01:14.506600
apply elapsed 0:02:11.290600
starmap async elapsed 0:01:27.718800
apply async elapsed 0:01:12.571200
# btw: 5k calls to Main_Residence in the main process looks as bad 
# as using apply for delegation
single elapsed 0:02:12.476800

Как видите, время выполнения различается, хотя все четыре метода выполняют одинаковую работу;apply_async выбранный вами, кажется, самый быстрый метод.

Стиль кодирования.Ваш код выглядит довольно ... нетрадиционно :) Вы используете Capitalized_Words_With_Underscore для своих имен (как для имен функций, так и для переменных), что в Python практически невозможно.Кроме того, присвоение имени Previous_List словарю ... сомнительно.Посмотрите PEP 8 , особенно раздел Соглашения об именах , чтобы увидеть общепринятый стиль кодирования для Python.

Судя по тому, как выглядит print, вы все еще используете Python 2. Я знаю, что в корпоративной или институциональной среде это иногда все, что у вас есть.Тем не менее, имейте в виду, что часы для Python 2 тикают

...