python:使用多处理共享巨大的词典

时间:2010-12-26 17:15:33

标签: python dictionary multiprocessing shared-objects

我正在使用多处理处理存储在字典中的大量数据。基本上我正在做的是加载一些签名,存储在字典中,从中构建一个共享的dict对象(获取Manager.dict()返回的'proxy'对象)并将此代理作为参数传递给具有该函数的函数在多处理中执行。

只是为了澄清:

signatures = dict()
load_signatures(signatures)
[...]
manager = Manager()
signaturesProxy = manager.dict(signatures)
[...]
result = pool.map ( myfunction , [ signaturesProxy ]*NUM_CORES )

现在,如果签名少于200万个条目,一切都会完美无缺。无论如何,我必须使用5.8M密钥处理字典(以二进制格式酸洗签名生成4.8 GB文件)。在这种情况下,进程在创建代理对象期间终止:

Traceback (most recent call last):
  File "matrix.py", line 617, in <module>
signaturesProxy = manager.dict(signatures)
  File "/usr/lib/python2.6/multiprocessing/managers.py", line 634, in temp
token, exp = self._create(typeid, *args, **kwds)
  File "/usr/lib/python2.6/multiprocessing/managers.py", line 534, in _create
id, exposed = dispatch(conn, None, 'create', (typeid,)+args, kwds)
  File "/usr/lib/python2.6/multiprocessing/managers.py", line 79, in dispatch
raise convert_to_error(kind, result)
multiprocessing.managers.RemoteError: 
---------------------------------------------------------------------------
Traceback (most recent call last):
  File "/usr/lib/python2.6/multiprocessing/managers.py", line 173, in handle_request
    request = c.recv()
EOFError
---------------------------------------------------------------------------

我知道数据结构很庞大,但我正在使用配备32GB RAM的机器,运行顶部我看到加载签名后的过程占用了7GB的RAM。然后它开始构建代理对象,RAM使用量上升到~17GB的RAM但从未接近32.此时,RAM使用率开始迅速减少,并且该过程因上述错误而终止。所以我想这不是因为内存不足错误......

有任何想法或建议吗?

谢谢,

的Davide

4 个答案:

答案 0 :(得分:6)

为什么不尝试使用数据库?数据库不限于可攻击/物理RAM,对于多线程/进程使用是安全的。

答案 1 :(得分:2)

为了节省时间而不必调试系统级问题,也许你可以将你的580万条记录字典分成三组,每组约200万,然后运行3次。

答案 2 :(得分:0)

我认为你遇到的问题是dict或hash表随着它的增长而自行调整大小。最初,dict有一定数量的桶可用。我不确定Python,但我知道Perl从8开始,然后当桶已满时,哈希值再重新创建8(即8,16,32,...)。

存储桶是哈希算法的着陆位置。 8个插槽并不意味着8个条目,它意味着8个内存位置。添加新项目时,会为该密钥生成一个哈希值,然后将其存储到该桶中。

这就是碰撞发挥作用的地方。存储桶中的项目越多,函数的速度就越慢,因为由于插槽的动态调整,项目会按顺序附加。

可能出现的一个问题是您的密钥非常相似并产生相同的散列结果 - 这意味着大多数密钥都在一个插槽中。预分配散列桶将有助于消除这种情况并实际上改善处理时间和密钥管理,而且不再需要进行所有交换。

但是,我认为你仍然局限于免费连续内存的数量,最终需要转到数据库解决方案。

旁注:我还是Python新手,我知道在Perl中你可以通过print%HASHNAME看到哈希统计信息,它会显示你的桶使用情况。帮助您识别冲突计数 - 您需要预先分配存储桶。这可以在Python中完成吗?

答案 3 :(得分:-3)

如果字典是只读的,则在大多数操作系统中不需要代理对象。

只需在启动工作人员之前加载字典,然后将它们放在可以访问的地方;最简单的地方是全局的模块。它们可以从工人那里读取。

from multiprocessing import Pool

buf = ""

def f(x):
    buf.find("x")
    return 0

if __name__ == '__main__':
    buf = "a" * 1024 * 1024 * 1024
    pool = Pool(processes=1)
    result = pool.apply_async(f, [10])
    print result.get(timeout=5)

这仅使用1GB的内存,而不是每个进程1GB,因为任何现代操作系统都会在fork之前创建数据的写入时复制阴影。请记住,其他工作人员不会看到对数据的更改,当然,会为您更改的任何数据分配内存。

它将使用一些内存:将修改包含引用计数的每个对象的页面,因此它将被分配。这是否重要取决于数据。

这适用于任何实现普通分叉的操作系统。它不适用于Windows;其(残缺的)流程模型需要为每个工作人员重新启动整个流程,因此不太擅长共享数据。