IPython.parallel模块中的内存泄漏?

时间:2012-01-11 05:25:21

标签: memory-leaks parallel-processing ipython

我正在使用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)

内存使用量持续增长,直到我最终耗尽(76GB内存)。我简化了evalPoint什么也不做,以确保它不是罪魁祸首。

eval的第一部分是从IPython的文档中复制的,关于如何使用负载均衡器。第二部分(解压缩和平均)是相当直接的,所以我认为这不会导致内存泄漏。此外,我尝试手动删除eval中的对象并致电gc.collect(),但没有运气。

我希望有IPython.parallel经验的人可以指出一些明显我做错的事情,或者能够确认这实际上是内存泄漏。

其他一些事实:

  • 我在Ubuntu 11.10上使用Python 2.7.2
  • 我正在使用IPython版本0.12
  • 我在服务器1-3上运行引擎,在服务器1上运行客户端和集线器。如果我将所有内容保存在服务器1上,我会得到类似的结果。
  • 我发现类似于IPython的内存泄漏的唯一一件事与%run有关,我相信在这个版本的IPython中已经修复了(同样,我没有使用%run

更新

此外,我尝试将记录从内存切换到SQLiteDB,以防出现问题,但仍然存在同样的问题。

响应(1)

内存消耗肯定在控制器中(我可以通过以下方式验证:(a)在另一台机器上运行客户端,以及(b)观察顶部)。我没有意识到非SQLiteDB仍会消耗内存,所以我没有打扰清除。

如果我使用DictDB并清除,我仍然会看到内存消耗增加,但速度要慢得多。 20次调用eval()时,它徘徊在2GB左右。

如果我使用MongoDB和purge,看起来mongod需要大约4.5GB的内存和ipcluster大约2.5GB。

如果我使用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 个答案:

答案 0 :(得分:7)

是控制器进程正在增长,还是客户端,或者两者兼而有之?

控制器会记住所有请求和所有结果,因此将此信息存储在简单dict中的默认行为将导致不断增长。使用数据库后端(sqlite或最好是mongodb,如果可用)应解决此问题,或者client.purge_results()方法可用于指示控制器丢弃任何/所有结果历史记录(如果您将删除它们,则将从数据库中删除它们)正在使用一个。)

客户端本身将其所有结果缓存在其results字典中,因此这也将导致随着时间的推移而增长。不幸的是,这个有点难以处理,因为引用可以在各种方向传播,并且不受控制器的db后端的影响。

这是IPython中的known issue,但是现在,您应该能够通过删除客户端结果/元数据中的条目来手动清除引用,如果您的视图一直存在,它就有自己的结果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()方法,因此您根本不需要将客户端传递给您的函数。应该可以通过View访问所有内容,如果您确实需要客户端(例如,用于清除缓存),则可以将其设置为view.client