Наибольшая глубина рекурсии с использованием Pickle/cPickle

Фон: я создаю trie для представления словаря, используя минимальный алгоритм построения. Список входных данных - это строки 4.3M utf-8, отсортированные лексикографически. Полученный граф ацикличен и имеет максимальную глубину 638 узлов. Первая строка моего script устанавливает предел рекурсии в 1100 через sys.setrecursionlimit().

Проблема: я хочу, чтобы иметь возможность сериализовать мой trie на диск, поэтому я могу загрузить его в память, не перестраивая с нуля (примерно 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 раз больше глубины trie для некоторого n? Или это может быть вызвано чем-то другим, о котором я не знаю?

Обновление: Установка глубины рекурсии на 3000 не помогла, поэтому этот проспект не выглядит многообещающим.

Обновление 2: Вы, ребята, были правы; Я был близоруким, полагая, что рассол будет использовать небольшую глубину вложенности из-за ограничений рекурсии по умолчанию. 10 000 сделали трюк.

Ответ 1

От документы:

Попытка рассортировать рекурсивную структуру данных может превышать максимальную глубину рекурсии, в этом случае будет поднят RuntimeError. Вы можете осторожно поднять этот предел с помощью sys.setrecursionlimit().

Хотя ваша реализация trie может быть простой, она использует рекурсию и может привести к проблемам при преобразовании в постоянную структуру данных.

Моя рекомендация будет продолжать повышать лимит рекурсии, чтобы увидеть, есть ли верхняя граница для данных, с которыми вы работаете, и реализации trie, которую вы используете.

В противном случае вы можете попытаться изменить свою реализацию дерева как "менее рекурсивную", если это возможно, или написать дополнительную реализацию, в которой встроена постоянная данных (используйте соленые огурцы и полки в вашей реализации). Надеюсь, что поможет

Ответ 2

Соленьям нужно рекурсивно пройти трю. Если Pickle использует только 5 уровней вызовов функций для выполнения вашей работы, то для вашей глубины 638 потребуется уровень, установленный более чем на 3000.

Попробуйте гораздо большее число, предел рекурсии действительно существует, чтобы защитить пользователей от необходимости слишком долго ждать, если рекурсия попадет в бесконечное отверстие.

Pickle обрабатывает циклы в порядке, поэтому это не имеет значения, даже если у вашего trie был цикл там

Ответ 3

Дважды проверьте, что ваша структура действительно ациклична.

Вы можете попробовать еще больше поднять лимит. Там жесткий максимум, зависящий от платформы, но попытка 50000 была бы разумной.

Также попробуйте мариновать небольшую версию своего трюка. Если маринование умирает, хотя оно хранит только пару трехбуквенных слов, то вы знаете, что есть какая-то фундаментальная проблема с вашим trie и не рассолом. Но если это происходит только тогда, когда вы пытаетесь сохранить 10k слов, то это может быть ошибка ограничения платформы в рассоле.

Ответ 4

Размер стека также должен быть увеличен с помощью resource.setrlimit, чтобы предотвратить segfault

Если вы используете только sys.setrecursionlimit, вы можете выполнить segfault, если достигнете максимального размера стека, разрешенного ядром Linux.

Это значение можно увеличить с помощью resource.setrlimit, как указано в: Настройка стекирования в python script

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 и как ее увеличить?

Максимальное значение по умолчанию для меня - 8 МБ.

Протестировано на Ubuntu 16.10, Python 2.7.12.

Ответ 5

Мои потребности были несколько мгновенными, поэтому я решил эту проблему, сохранив словарь в формате .txt. Единственное, что при повторной загрузке файла необходимо преобразовать обратно в словарь.

import json

# Saving the dictionary
with open('filename.txt', 'w') as file_handle:
    file_handle.write(str(dictionary))

# Importing the .txt file
with open('filename.txt', 'r') as file_handle:
    f = '"' + file_handle.read() + '"'

# From .txt file to dictionary
dictionary = eval(json.loads(f))

Если это не работает, вы можете попробовать экспортировать словарь в формате json.