Высокое использование памяти для пула в цикле for

У меня есть цикл for с 2 пулами:

if __name__ == '__main__':
    for length in range(1, 15, 5):

        def map_CCWP(it):
            return CCWP(G, length, Ep)
        pool = multiprocessing.Pool(processes=multiprocessing.cpu_count())
        Scores = pool.map(map_CCWP, range(R))

        S = []
        # some work to get S

        def map_AvgIAC (it):
            return avgIAC(G, S, Ep, I) 
        pool2 = multiprocessing.Pool(processes=multiprocessing.cpu_count())
        T = pool2.map(map_AvgIAC, range(4))

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

Другой вариант заключался в том, чтобы поставить пулы под условие:

if pool == None:
    pool = multiprocessing.Pool(processes=multiprocessing.cpu_count())

и он действительно не использует так много памяти. Однако аргументы каждой итерации для функций map_CCWP и map_AvgIAC меняются, и, как я обнаружил, pool.map будет использовать map_CCWP с начальным length и map_AvgIAC с начальным S.

Как я могу запустить пул для функций, которые меняются на каждой итерации и не увеличивают использование памяти?


person Sergey Ivanov    schedule 03.06.2014    source источник


Ответы (2)


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

So:

pool = multiprocessing.Pool(processes=None, maxtasksperchild=1)

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

Обратите внимание, что я определяю процессы как None. Это то же самое, что и использование multiprocessing.cpu_count(), но менее подробное.

В другой ситуации я столкнулся с тем, что иногда (один или два раза на миллионы) память случайного ребенка выходит из-под контроля, машина начинает подкачиваться, и все зависает или сильно тормозит. Мой обходной путь был следующим:

iterations = int(math.ceil(total / b_size))

for block in xrange(iterations):
    restricted_iterator = iterator[block * b_size:(block + 1) * b_size]
    # This works because a slice can end beyond the length of the list.

    pool = multiprocessing.Pool(processes=None, maxtasksperchild=1)
    try:
        peaks = pool.map(caller, restricted_iterator)
    except Exception as e:
        raise e    # I don't expect this to ever happen.
    finally:
        pool.terminate()
                   # Kill the pool very dead.
    # Save the data to disk and free memory.

Я разбиваю работу на куски и обрабатываю каждый. Таким образом, если когда-либо один из этих «сумасшедших детей» накапливает память, остальные закончат работу через несколько минут, заблудший ребенок некоторое время будет один, имея больше оперативной памяти. Как следствие, она завершится менее чем за пару минут, так что общая задержка программы не такая уж и большая. Настроив b_size, я могу контролировать, как часто я убираюсь. (В моем случае, разбивая на 10-20 блоков и сохраняя на диск в середине, я по-прежнему сохраняю среднее использование ЦП около 97%, так что не так много потеряно).

person Davidmh    schedule 03.06.2014
comment
Давайте обсудим первую часть вашего ответа. Должен ли я использовать pool = multiprocessing.Pool(processes=None, maxtasksperchild=1) без каких-либо условий, которые я описал? Должен ли я как-то освобождать память в конце каждой итерации? Прямо сейчас я все еще наблюдаю увеличение памяти после каждой итерации. - person Sergey Ivanov; 03.06.2014
comment
Использование этой строки гарантирует, что никакие объекты Python не будут загромождать вашу память, что должно занять у вас большую часть пути. Вы также должны terminate пул после его завершения, чтобы быть уверенным. Возможно, CCWP имеет утечку памяти, вы можете проверить это, повторно запустив без пула. Проблема также может заключаться в том, что вывод сам по себе большой, поэтому память должна расти. Я не вижу, как использование условия поможет. - person Davidmh; 03.06.2014
comment
О CCWP утечке. Вы имеете в виду просто использовать map вместо pool.map? У меня все еще есть прирост памяти в этом случае, хотя и не такой большой. С pool.map у меня 2.3Гб, без 1.8Гб. Про выход большой. После каждой итерации я просто хочу знать, что T это просто целое число. Все остальное можно освободить. Должен ли я все еще ожидать увеличения памяти в этом случае? - person Sergey Ivanov; 03.06.2014
comment
Итак, CCWP утекает 500 Мб из-за многопроцессорности и 1,8 Гб сам по себе. Сначала исправьте CCWP, а затем беспокойтесь о 500 МБ (если только 1,8 ГБ не являются выходными данными). T не может быть целым числом, как вы показываете, это список из четырех элементов. Вы можете del большие объекты, когда перестанете их использовать, и обертывание каждой итерации в функцию сделает это за вас в конце; что-то вроде: compute_T(length) for length in... - person Davidmh; 03.06.2014
comment
Подождите.. но у меня 1.3Gb используется еще до того, как я начну все запускать. Во время работы он может дойти до использования всей памяти (~ 5Gb). Насчет T. Вы правы: T — это список, затем я беру его норму и получаю число. В любом случае я использую map_CCWP, Scores, S, pool на каждой итерации, но они разные для всех итераций. Поэтому, как только я их использовал, они мне не нужны; но я перезаписываю их и думал, что на каждой итерации будет использоваться один и тот же объем памяти. Разве это не так? - person Sergey Ivanov; 03.06.2014
comment
Да, они должны быть полностью перезаписаны, но если вас могут укусить некоторые циклические ссылки. - person Davidmh; 03.06.2014

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

person RayLuo    schedule 01.12.2014