iPython 并行模块的内存消耗过大

10

我正在使用ipyparallel模块来加速全量列表比较,但是我遇到了巨大的内存消耗问题。

这里是我正在运行的脚本的简化版本:

从SLURM脚本开始启动集群并运行Python脚本。

ipcluster start -n 20 --cluster-id="cluster-id-dummy" &
sleep 60
ipython /global/home/users/pierrj/git/python/dummy_ipython_parallel.py
ipcluster stop --cluster-id="cluster-id-dummy"

在Python中,为简化示例创建两个列表的列表

import ipyparallel as ipp
from itertools import compress

list1 = [ [i, i, i] for i in range(4000000)]
list2 = [ [i, i, i] for i in range(2000000, 6000000)]

然后定义我的列表比较函数:

def loop(item):
    for i in range(len(list2)):
        if list2[i][0] == item[0]:
            return True
    return False

然后连接到我的ipython引擎,将list2推送到每个引擎并映射我的函数:

rc = ipp.Client(profile='default', cluster_id = "cluster-id-dummy")
dview = rc[:]
dview.block = True
lview = rc.load_balanced_view()
lview.block = True
mydict = dict(list2 = list2)
dview.push(mydict)
trueorfalse = list(lview.map(loop, list1))

如上所述,我正在使用SLURM在集群上运行此程序,并从sacct命令获取内存使用情况。下面是我在每个步骤中得到的内存使用情况:

仅创建两个列表:1.4 Gb 创建两个列表并将它们推送到20个引擎:22.5 Gb 全部过程:62.5 Gb++(这是我遇到OUT_OF_MEMORY故障的地方)

通过在运行作业时在节点上运行htop,看起来内存使用率会随着时间的推移而缓慢增加,直到达到最大内存并失败。

我浏览了一下之前的帖子,并实施了一些建议的解决方案,但没有成功

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

我尝试了每次循环都清除视图:

def loop(item):
    lview.results.clear()
    for i in range(len(list2)):
        if list2[i][0] == item[0]:
            return True
    return False

我尝试在每个循环中清除客户端:

def loop(item):
    rc.purge_everything()
    for i in range(len(list2)):
        if list2[i][0] == item[0]:
            return True
    return False

我尝试使用ipcontroller的--nodb和--sqlitedb标志,以以下方式启动我的集群:

--nodb和--sqlitedb标志被用于ipcontroller进程的启动,但是具体的启动命令未给出。请提供完整的启动命令或上下文以获取更准确的翻译。

如果需要翻译其他内容,请重新提交请求并提供原始文本。

ipcontroller --profile=pierrj --nodb --cluster-id='cluster-id-dummy' &
sleep 60
for (( i = 0 ; i < 20; i++)); do ipengine --profile=pierrj --cluster-id='cluster-id-dummy' & done
sleep 60
ipython /global/home/users/pierrj/git/python/dummy_ipython_parallel.py
ipcluster stop --cluster-id="cluster-id-dummy" --profile=pierrj

很不幸,这些都没有帮助,导致了完全相同的内存不足错误。

任何建议或帮助将不胜感激!

2个回答

2
看看周围,似乎有很多人抱怨LoadBalancedViews非常浪费内存,我没有找到任何有用的建议来解决这个问题,例如这里
然而,我认为根据您的示例,那不是开始的地方。我假设您的示例是您代码的合理近似值。如果您的代码正在进行几百万数据点的列表比较,我建议您使用类似numpy的东西来执行计算,而不是在python中迭代。
如果您重构算法以使用numpy矢量操作,它将比从列表索引并在python中执行计算要快得多。 numpy是一个C库,库内计算将受益于编译时优化。此外,在数组上执行操作还受益于处理器预测缓存(您的CPU希望您向前使用相邻的内存并预加载它;如果您分散访问数据,则可能会失去此好处)。
我对您的示例进行了非常快速的黑客,以证明这一点。此示例将您的loop计算与同一问题的非常天真的numpy实现进行了比较。小条目时,python循环方法具有竞争力,但是随着您处理的条目数量的增加,速度很快就会快100倍。我怀疑您结构化数据的方式将超过通过并行获得的性能提升。
请注意,我选择了分布中间的匹配值;性能差异显然取决于分布。
import numpy as np
import time

def loop(item, list2):
    for i in range(len(list2)):
        if list2[i][0] == item[0]:
            return True
    return False

def run_comparison(scale):
    list2 = [ [i, i, i] for i in range(4 * scale)]
    arr2 = np.array([i for i in range(4 * scale)])

    test_value = (2 * scale)
    np_start = time.perf_counter()
    res1 = test_value in arr2
    np_end = time.perf_counter()
    np_time = np_end - np_start

    loop_start = time.perf_counter()
    res2 = loop((test_value, 0, 0), list2)
    loop_end = time.perf_counter()
    loop_time = loop_end - loop_start

    assert res1 == res2

    return (scale, loop_time / np_time)

print([run_comparison(v) for v in [100, 1000, 10000, 100000, 1000000, 10000000]])

返回:

[
  (100, 1.0315526939407524),
  (1000, 19.066806587378263),
  (10000, 91.16463510672537),
  (100000, 83.63064249916434),
  (1000000, 114.37531283123414),
  (10000000, 121.09979997458508)
]

1
谢谢您的解释!当时我不熟悉numpy,现在已经在我的代码中实现了它。同样地,我最终使用了GNU parallel。尽管如此,我仍然保留了这篇文章,因为这似乎是iPython Parallel的一个普遍问题,其他人也需要帮助。 - Pierre Joubert

1
假设正在将两个列表中的一个任务分配给工作线程,您将希望确保各个工作线程正在使用相同的列表副本。在大多数情况下,似乎 ipython 并行将 pickle 对象发送到工作线程(相关文档请参见 relevant doc)。如果您能够使用其中一种不会被复制的类型(如文档中所述)

缓冲区/内存视图,字节对象和 numpy 数组。

由于引用被分发,可能解决内存问题。此答案还假设单个任务在工作时不需要操作列表(线程安全)。
简而言之,将传递给并行工作者的对象移动到 numpy 数组中似乎可以解决内存爆炸问题。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接