使用Pickle / cPickle达到最大递归深度
背景:我正在构建一个字典的前缀树(trie),使用一种简单的构建算法。输入的列表包含430万个utf-8字符串,并且这些字符串是按字典顺序排列的。最终生成的图是无环的,最大深度为638个节点。我的脚本第一行通过sys.setrecursionlimit()
将递归限制设置为1100。
问题:我希望能够将我的前缀树保存到磁盘上,这样我就可以在需要时直接加载到内存中,而不必从头开始重建(大约需要22分钟)。我尝试了pickle.dump()
和cPickle.dump()
,使用了文本和二进制两种协议。每次操作后,我都会得到一个类似下面的错误信息:
File "/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/pickle.py", line 649, in save_dict
self._batch_setitems(obj.iteritems())
File "/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/pickle.py", line 663, in _batch_setitems
save(v)
File "/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/pickle.py", line 286, in save
f(self, obj) # Call unbound method with explicit self
File "/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/pickle.py", line 725, in save_inst
save(stuff)
File "/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/pickle.py", line 286, in save
f(self, obj) # Call unbound method with explicit self
File "/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/pickle.py", line 648, in save_dict
self.memoize(obj)
RuntimeError: maximum recursion depth exceeded
我的数据结构相对简单:trie
包含一个起始状态的引用,并定义了一些方法。dfa_state
包含一个布尔字段、一个字符串字段,以及一个从标签到状态的字典映射。
我对pickle
的内部工作原理不是很了解,难道我的最大递归深度需要大于或等于前缀树深度的n倍吗?还是说这可能是由于我不知道的其他原因造成的?
更新:将递归深度设置为3000并没有帮助,所以这个方向看起来不太有希望。
更新2:你们说得对;我之前太短视了,以为pickle会因为默认的递归限制而使用较小的嵌套深度。设置为10,000就解决了问题。
6 个回答
为了防止程序崩溃,栈的大小也必须通过 resource.setrlimit
来增加
如果你只使用 sys.setrecursionlimit
,当达到Linux内核允许的最大栈大小时,仍然可能会导致程序崩溃。
这个值可以通过 resource.setrlimit
来增加,具体可以参考这里: 在Python脚本中设置栈大小
import pickle
import resource
import sys
print resource.getrlimit(resource.RLIMIT_STACK)
print sys.getrecursionlimit()
max_rec = 0x100000
# May segfault without this line. 0x100 is a guess at the size of each stack frame.
resource.setrlimit(resource.RLIMIT_STACK, [0x100 * max_rec, resource.RLIM_INFINITY])
sys.setrecursionlimit(max_rec)
a = []
# 0x10 is to account for subfunctions called inside `pickle`.
for i in xrange(max_rec / 0x10):
a = [a]
print pickle.dumps(a, -1)
另外可以查看: Python中的最大递归深度是多少,如何增加它?
对我来说,默认的最大值是8MB。
在Ubuntu 16.10和Python 2.7.12上测试过。
Pickle确实需要递归地遍历你的字典树。如果Pickle只用了5层函数调用来完成工作,而你的字典树深度有638层,那就需要把层数设置到3000以上。
试试一个更大的数字,递归限制其实是为了保护用户,避免在递归过程中陷入无限循环而让人等得太久。
Pickle对循环的处理还不错,所以即使你的字典树里有循环也没关系。