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

Abe*_*der 4 parallel-processing memory-leaks ipython

我正在使用IPython.parallel来处理集群上的大量数据.我运行的远程功能如下:

def evalPoint(point, theta):
    # do some complex calculation
    return (cost, grad)
Run Code Online (Sandbox Code Playgroud)

这个函数调用它:

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)
Run Code Online (Sandbox Code Playgroud)

如果我运行代码:

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)
Run Code Online (Sandbox Code Playgroud)

内存使用量不断增长,直到我最终耗尽(76GB内存).evalPoint为了确保它不是罪魁祸首,我已经简化为什么都不做.

第一部分eval是从IPython的文档中复制的,关于如何使用负载均衡器.第二部分(解压缩和平均)是相当直接的,所以我认为这不会导致内存泄漏.此外,我已经尝试手动删除对象evalgc.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
Run Code Online (Sandbox Code Playgroud)

所以,我想如果我使用DictDB,我可能会好的(我今晚要尝试一下).我不确定是否仍然需要一些内存消耗(我也像你建议的那样在客户端中清除).

min*_*nrk 7

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

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

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

这是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]
Run Code Online (Sandbox Code Playgroud)

或者,您可以使用简单的方法清除整个客户端缓存dict.clear():

view.results.clear()
client.results.clear()
client.metadata.clear()
Run Code Online (Sandbox Code Playgroud)

边注:

视图有自己的wait()方法,因此您根本不需要将Client传递给您的函数.一切都应该可以通过View访问,如果你真的需要客户端(例如用于清除缓存),你可以得到它view.client.