Утечка памяти в модуле IPython.parallel? - PullRequest
4 голосов
/ 11 января 2012

Я использую IPython.parallel для обработки большого количества данных в кластере. Дистанционная функция, которую я запускаю, выглядит следующим образом:

def evalPoint(point, theta):
    # do some complex calculation
    return (cost, grad)

, который вызывается этой функцией:

def eval(theta, client, lview, data):
    async_results = []
    for point in data:
        # evaluate current data point
        ar = lview.apply_async(evalPoint, point, theta)
        async_results.append(ar)

    # wait for all results to come back
    client.wait(async_results)

    # and retrieve their values
    values = [ar.get() for ar in async_results]

    # unzip data from original tuple
    totalCost, totalGrad = zip(*values)

    avgGrad =  np.mean(totalGrad, axis=0)
    avgCost = np.mean(totalCost, axis=0)

    return (avgCost, avgGrad)

Если я запускаю код:

client = Client(profile="ssh")
client[:].execute("import numpy as np")        

lview = client.load_balanced_view()

for i in xrange(100):
    eval(theta, client, lview, data)

использование памяти продолжает расти до тех пор, пока у меня не закончится (76 ГБ памяти). Я упростил evalPoint, чтобы ничего не делать, чтобы убедиться, что это не преступник.

Первая часть eval была скопирована из документации IPython о том, как использовать балансировщик нагрузки. Вторая часть (распаковка и усреднение) довольно проста, поэтому я не думаю, что это ответственно за утечку памяти. Кроме того, я попытался вручную удалить объекты в eval и безуспешно вызывать gc.collect().

Я надеялся, что кто-то, имеющий опыт работы с IPython.parallel, мог бы указать на очевидное, что я делаю неправильно, или смог бы подтвердить это на самом деле утечкой памяти.

Некоторые дополнительные факты:

  • Я использую Python 2.7.2 в Ubuntu 11.10
  • Я использую IPython версии 0.12
  • У меня есть двигатели, работающие на серверах 1-3, а клиент и концентратор работают на сервере 1. Я получаю аналогичные результаты, если все храню только на сервере 1.
  • Единственное, что я нашел похожим на утечку памяти для IPython, было связано с %run, который, я считаю, был исправлен в этой версии IPython (также я не использую %run)

обновление

Кроме того, я попытался переключить ведение журнала из памяти на SQLiteDB, в случае, если это было проблемой, но все еще с той же проблемой.

ответ (1)

Потребление памяти определенно находится в контроллере (я мог убедиться в этом: (а) запустив клиент на другой машине и (б) просмотрев топ). Я не осознавал, что не SQLiteDB будет по-прежнему потреблять память, поэтому я не потрудился на очистку.

Если я использую DictDB и purge, я все равно вижу, как увеличивается потребление памяти, но гораздо медленнее. Это было около 2 ГБ для 20 вызовов eval ().

Если я использую MongoDB и purge, похоже, что mongod занимает около 4,5 ГБ памяти, а ipcluster - около 2,5 ГБ.

Если я использую SQLite и пытаюсь выполнить очистку, я получаю следующую ошибку:

File "/usr/local/lib/python2.7/dist-packages/IPython/parallel/controller/hub.py", line 1076, in purge_results
  self.db.drop_matching_records(dict(completed={'$ne':None}))
File "/usr/local/lib/python2.7/dist-packages/IPython/parallel/controller/sqlitedb.py", line 359, in drop_matching_records
  expr,args = self._render_expression(check)
File "/usr/local/lib/python2.7/dist-packages/IPython/parallel/controller/sqlitedb.py", line 296, in _render_expression
  expr = "%s %s"%null_operators[op]
TypeError: not enough arguments for format string

Итак, я думаю, что если я буду использовать DictDB, у меня все будет хорошо (я собираюсь попробовать бегать сегодня вечером). Я не уверен, ожидается ли некоторое потребление памяти или нет (я также очищаю клиент, как вы предложили).

1 Ответ

7 голосов
/ 12 января 2012

Это процесс контроллера, который растет, или клиент, или оба?

Контроллер запоминает все запросы и все результаты, поэтому поведение по умолчанию при сохранении этой информации в простом формате приводит к постоянному росту. Использование бэкэнда БД (sqlite или предпочтительно mongodb, если доступно) должно решить эту проблему, или можно использовать метод client.purge_results(), чтобы указать контроллеру сбросить любую / всю историю результатов (это удалит их из БД, если вы используете один).

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

Это известная проблема в IPython, но на данный момент вы должны быть в состоянии очистить ссылки вручную, удалив записи в клиентских диктатах результатов / метаданных, и если ваше представление застревает, оно имеет свои результаты dict:

# ...
# and retrieve their values
values = [ar.get() for ar in async_results]

# clear references to the local cache of results:
for ar in async_results:
    for msg_id in ar.msg_ids:
        del lview.results[msg_id]
        del client.results[msg_id]
        del client.metadata[msg_id]

Или вы можете очистить весь кэш на стороне клиента с помощью простого dict.clear():

view.results.clear()
client.results.clear()
client.metadata.clear()

Примечание:

Представления имеют собственный метод wait (), поэтому вам вообще не нужно передавать Client в вашу функцию. Все должно быть доступно через представление, и если вам действительно нужен клиент (например, для очистки кэша), вы можете получить его как view.client.

...