Итерация по pool.imap_unordered - PullRequest
5 голосов
/ 25 марта 2012

Рассмотрим очень простой код:

#!/usr/bin/python

from multiprocessing import Pool
import random

def f(x):
    return x*x

def sampleiter(n):
    num = 0
    while num < n:
     rand = random.random()
     yield rand
     num += 1

if __name__ == '__main__':
    pool = Pool(processes=4)              # start 4 worker processes
    for item in pool.imap_unordered(f, sampleiter(100000000000000), 20):
     print item
    pool.close

При работе в терминале у Python происходит утечка памяти.
Что может быть не так?

Ответы [ 2 ]

4 голосов
/ 25 марта 2012

Буферизация вывода не является проблемой (или, по крайней мере, не единственной), потому что (a) сам процесс Python растет в памяти, и (b) если вы перенаправляете на /dev/null, это все равно происходит.

Я думаю, что проблема в том, что когда вы распечатываете результаты, пул возвращает результаты гораздо быстрее, чем они могут быть использованы, и поэтому в памяти остается множество результатов. Если вы посмотрите на источник класса, который делает это , промежуточные результаты будут сохранены в collections.deque, называемом _items; Держу пари, что _items становится огромным.

Я не совсем уверен, как это проверить, потому что, хотя imap_unordered возвращает экземпляр этого класса , вы все равно, похоже, сможете получить только методы генератора:

In [8]: r = pool.imap_unordered(f, sampleiter(1e8), 20)

In [9]: print dir(r)
['__class__', '__delattr__', '__doc__', '__format__', '__getattribute__', '__hash__',
 '__init__', '__iter__', '__name__', '__new__', '__reduce__', '__reduce_ex__', 
 '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 
 'close', 'gi_code', 'gi_frame', 'gi_running', 'next', 'send', 'throw']

Обновление: Если вы добавите time.sleep(.01) к f(), использование памяти останется полностью постоянным. Итак, проблема в том, что вы производите результаты быстрее, чем можете их использовать.

(В качестве отступления: вы имеете в виду pool.close() в конце примера кода; pool.close - это просто ссылка на функцию и фактически не вызывает ее.)

2 голосов
/ 25 марта 2012

Единственная переменная, которая вызывает утечку памяти, - это оператор print.Когда я заменяю print item на pass, память остается низкой и постоянной.Я не уверен, что именно происходит под капотом, когда вы печатаете, но это явно что-то складывает, а не освобождает.Кроме того, когда я уменьшаю размер вашего чанка до 1, память увеличивается намного медленнее (очевидно), но также занимает больше времени.Таким образом, он умножает использование памяти.

Обновление

Нашел это как конкретную ссылку на увеличение использования памяти из-за буфера истории терминала (не самого процесса python): Утечка памятипри запуске python в Mac OS Terminal

...