Как избежать вычислений при каждой перезагрузке модуля python - PullRequest
6 голосов
/ 12 октября 2008

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

Чтобы получить больше информации о моей проблеме,

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

Ответы [ 13 ]

17 голосов
/ 13 октября 2008

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

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

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

# 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

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

3 голосов
/ 12 октября 2008

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

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 .

2 голосов
/ 26 июля 2009

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

2 голосов
/ 06 декабря 2008

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

2 голосов
/ 12 октября 2008

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

Поддерживаются не все типы объектов Python; в общем, этот модуль может записывать и читать только объекты, значение которых не зависит от конкретного вызова Python. Поддерживаются следующие типы: Нет, целые числа, длинные целые числа, числа с плавающей запятой, строки, объекты Unicode, кортежи, списки, наборы, словари и объекты кода, где следует понимать, что кортежи, списки и словари поддерживаются только до тех пор, пока поскольку содержащиеся в нем значения сами поддерживаются; и рекурсивные списки и словари не должны быть написаны (они вызовут бесконечные циклы).

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

2 голосов
/ 12 октября 2008

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

1 голос
/ 06 февраля 2010

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

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

word = r.get(key)
1 голос
/ 14 октября 2008
  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. 
    
1 голос
/ 13 октября 2008

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

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

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

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

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

1 голос
/ 12 октября 2008

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

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

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