Как избежать вычислений каждый раз при перезагрузке модуля Python

У меня есть модуль python, который использует огромную глобальную переменную словаря, в настоящее время я помещаю вычислительный код в верхнюю часть, каждый первый импорт или перезагрузка модуля занимает более одной минуты, что совершенно неприемлемо. Как я могу где-нибудь сохранить результат вычислений, чтобы при следующем импорте / перезагрузке не приходилось его вычислять? Я попробовал cPickle, но загрузка словарной переменной из файла (1,3 МБ) занимает примерно столько же времени, сколько и вычисление.

Чтобы предоставить больше информации о моей проблеме,

FD = FreqDist(word for word in brown.words()) # this line of code takes 1 min

Не могли бы вы прояснить: нужна ли вам постоянство между вызовами разных интерпретаторов Python или вы спрашиваете о множественном импорте в одном экземпляре интерпретатора?

jfs 12.10.2008 21:47

Я согласен со следующими ответами, но вам действительно следует предоставить более подробную информацию о типе данных, о том, как они создаются и для чего они предназначены (цель и процесс), поскольку правильное решение во многом зависит от этого.

e-satis 13.10.2008 09:13

Я добавил строку кода выше.

btw0 13.10.2008 18:16
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
6
3
3 130
13
Перейти к ответу Данный вопрос помечен как решенный

Ответы 13

Я предполагаю, что вы вставили буквальный dict в источник, и это занимает минуту? Я не знаю, как это обойти, но вы, вероятно, могли бы избежать создания этого dict на импорт ... Вы могли бы лениво создать его при первом фактическом использовании.

FD = FreqDist (слово в слово в brown.words ()) # эта строка кода занимает одну минуту

btw0 12.10.2008 20:25

Если FD каждый раз одно и то же, вставьте результаты «print FD» прямо в исходный файл и пропустите этап вычислений.

David Grant 12.10.2008 20:30

FD - это огромный экземпляр стороннего класса, который нельзя записать буквально.

btw0 12.10.2008 21:01

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

Shelve также будет обрабатывать значения dict, но будет выполнять (un) pickle не при запуске для всех элементов, а только во время доступа к каждому элементу.

Рассчитайте глобальную переменную при первом использовании.

class Proxy:
    @property
    def global_name(self):
        # calculate your global var here, enable cache if needed
        ...

_proxy_object = Proxy()
GLOBAL_NAME = _proxy_object.global_name

Или, что еще лучше, доступ к необходимым данным через специальный объект данных.

class Data:
    GLOBAL_NAME = property(...)

data = Data()

Пример:

from some_module import data

print(data.GLOBAL_NAME)

См. Настройки Django.

Вы можете попробовать использовать модуль маршал вместо модуля c? Pickle; это могло быть быстрее. Этот модуль используется python для хранения значений в двоичном формате. Обратите особое внимание на следующий абзац, чтобы узнать, подходит ли маршал вашим потребностям:

Not all Python object types are supported; in general, only objects whose value is independent from a particular invocation of Python can be written and read by this module. The following types are supported: None, integers, long integers, floating point numbers, strings, Unicode objects, tuples, lists, sets, dictionaries, and code objects, where it should be understood that tuples, lists and dictionaries are only supported as long as the values contained therein are themselves supported; and recursive lists and dictionaries should not be written (they will cause infinite loops).

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

Расширяя идею отложенного вычисления, почему бы не превратить dict в класс, который предоставляет (и кэширует) элементы по мере необходимости?

Вы также можете использовать psyco для ускорения общего выполнения ...

Вы также можете проверить pyrex / cython.

Jason Baker 13.10.2008 02:42

Психо, кажется, работает только на 32-битных системах

James 06.02.2010 19:25

ИЛИ ЖЕ вы могли бы просто использовать базу данных для хранения значений? Обратите внимание на SQLObject, который позволяет очень легко сохранять данные в базе данных.

Ответ принят как подходящий

Чтобы уточнить: код в теле модуля - это нет, который выполняется каждый раз, когда модуль импортируется - он запускается только один раз, после чего будущий импорт находит уже созданный модуль, а не воссоздает его. Взгляните на sys.modules, чтобы увидеть список кэшированных модулей.

Однако, если ваша проблема - это время, необходимое для первого импорта после запуска программы, вам, вероятно, придется использовать какой-то другой метод, кроме python dict. Вероятно, лучше всего было бы использовать дисковую форму, например базу данных sqlite, один из модулей dbm.

Для минимального изменения вашего интерфейса лучшим вариантом может быть модуль полки - это обеспечивает довольно прозрачный интерфейс между модулями dbm, который заставляет их действовать как произвольный Python dict, позволяя сохранять любое выбираемое значение. Вот пример:

# Create dict with a million items:
import shelve
d = shelve.open('path/to/my_persistant_dict')
d.update(('key%d' % x, x) for x in xrange(1000000))
d.close()

Затем используйте его в следующем процессе. Не должно быть большой задержки, так как поиск выполняется только для ключа, запрошенного в форме на диске, поэтому все не должно загружаться в память:

>>> d = shelve.open('path/to/my_persistant_dict')
>>> print d['key99999']
99999

Это немного медленнее, чем настоящий dict, и воля по-прежнему долго загружается, если вы делаете что-то, для чего требуются все ключи (например, пытаетесь распечатать его), но может решить вашу проблему.

Несколько вещей, которые помогут ускорить импорт:

  1. Вы можете попробовать запустить python с флагом -OO при запуске python. Это приведет к некоторой оптимизации, которая сократит время импорта модулей.
  2. Есть ли причина, по которой вы не можете разбить словарь на более мелкие словари в отдельных модулях, которые можно загружать быстрее?
  3. В крайнем случае, вы можете выполнять вычисления асинхронно, чтобы они не задерживали вашу программу до тех пор, пока ей не потребуются результаты. Или, может быть, даже поместите словарь в отдельный процесс и передавайте данные туда и обратно с помощью IPC, если вы хотите воспользоваться преимуществами многоядерных архитектур.

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

  1. Вы импортируете модуль внутри функции? Если это так, этот может приведет к проблемам с производительностью, поскольку он должен проверять и видеть, загружается ли модуль каждый раз, когда он попадает в оператор импорта.
  2. Ваша программа многопоточная? Я видел случаи, когда выполнение кода при импорте модуля в многопоточном приложении может вызывать некоторую нестабильность и нестабильность приложения (особенно с модулем cgitb).
  3. Если это глобальная переменная, имейте в виду, что время поиска глобальной переменной может быть значительно больше, чем время поиска локальной переменной. В этом случае вы можете добиться значительного повышения производительности, привязав словарь к локальной переменной, если вы используете его несколько раз в одном контексте.

С учетом сказанного, немного сложно дать вам какой-либо конкретный совет без небольшого дополнительного контекста. В частности, куда вы это импортируете? А какие вычисления?

  1. Выделите вычислительно-интенсивную часть в отдельный модуль. Тогда, по крайней мере, при перезагрузке ждать не придется.

  2. Попробуйте сбросить структуру данных по протоколу 2. Попробуйте выполнить команду cPickle.dump(FD, protocol=2). Из строки документации для cPickle.Pickler:

    Protocol 0 is the
    only protocol that can be written to a file opened in text
    mode and read back successfully.  When using a protocol higher
    than 0, make sure the file is opened in binary mode, both when
    pickling and unpickling. 
    

Кажется, что наличие кода в отдельном модуле не очень помогает, при перезагрузке этого модуля код в отдельном модуле снова запускается.

btw0 14.10.2008 21:06

Если решение «полки» оказывается слишком медленным или неудобным, есть другие возможности:

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

Итак ... выполнение чего-то подобного гарантирует, что этот код будет выполнен только один раз.

try:
    FD
except NameError:
    FD = FreqDist(word for word in brown.words())

shelve работает очень медленно с большими наборами данных. Я довольно успешно использовал Redis и написал вокруг него Оболочка FreqDist. Это очень быстро, и к нему можно обращаться одновременно.

Я прохожу через ту же проблему ... полки, базы данных и т. д. слишком медленны для этого типа проблем. Вам нужно будет принять удар один раз, вставить его в хранилище ключей / val inmemory, например Redis. Он просто будет жить там в памяти (предупреждаем, что он может использовать хороший объем памяти, поэтому вам может понадобиться специальный ящик). Вам никогда не придется перезагружать его, и вы просто будете искать в памяти ключи

r = Redis()
r.set(key, word)

word = r.get(key)

Другие вопросы по теме