Эффективная memoization в Python
у меня есть задача, которую нужно решить, и самая важная часть на данный момент-сделать сценарий максимально эффективным по времени. Одним из элементов, которые я пытаюсь оптимизировать, является memoization в одной из функций.
Итак, мой вопрос: какой из следующих 3-4 методов является наиболее эффективным / быстрым методом реализации memoization в Python?
я предоставил код только в качестве примера-если один из методов более эффективен, но не в дело, о котором я упоминал, пожалуйста, поделитесь тем, что вы знаете.
Решение 1 - Использование изменяемой переменной из внешней области
это решение часто показано в качестве примера memoization, но я не уверен, насколько оно эффективно. Я слышал, что использование глобальных переменных (в данном случае это переменная из внешней, а не глобальной области) менее эффективно.
def main():
memo = {}
def power_div(n):
try:
return memo[n]
except (KeyError):
memo[n] = (n ** 2) % 4 # example expression, should not matter
return memo[n]
# extensive usage of power_div() here
решение 2-Использование изменяемого по умолчанию аргумента
Я нашел где-то, что использование изменяемого по умолчанию аргументы использовались в прошлом для передачи переменных из внешней области, когда Python искал переменную сначала в локальной области, а затем в глобальной области, пропуская нелокальную область (в этом случае область внутри функции main()
). Поскольку аргумент по умолчанию инициализируется только в момент определения функции и доступен только внутри внутренней функции, может быть, он более эффективен?
def main():
def power_div(n, memo={}):
try:
return memo[n]
except (KeyError):
memo[n] = (n ** 2) % 4 # example expression, should not matter
return memo[n]
# extensive usage of power_div() here
или, может быть, следующая версия (будучи на самом деле комбинацией решений 1&2) более эффективно?
def main():
memo = {}
def power_div(n, memo=memo):
try:
return memo[n]
except (KeyError):
memo[n] = (n ** 2) % 4 # example expression, should not matter
return memo[n]
# extensive usage of power_div() here
решение 3-атрибут функции
это еще один довольно распространенный пример memoization в Python - объект memoization хранится как атрибут самой функции.
def main():
def power_div(n):
memo = power_div.memo
try:
return memo[n]
except (KeyError):
memo[n] = (n ** 2) % 4 # example expression, should not matter
return memo[n]
# extensive usage of power_div() here
резюме
меня очень интересует ваше мнение об этих четырех решений для мемоизации. Важно также, что функция, которая использует мемоизацию в другой функции.
Я знаю что есть и другие решения для мемоизации (например,Memoize
декоратор), но мне трудно поверить, что это более эффективное решение, чем перечисленные выше. Поправьте меня, если я ошибаюсь.
спасибо заранее.
2 ответов
различные стили доступа к переменным уже были синхронизированы и сравнены по адресу: http://code.activestate.com/recipes/577834-compare-speeds-of-different-kinds-of-access-to-var Вот краткое резюме: локальный доступ бьет нелокальные (вложенные области), которые бьют глобальный доступ (область модуля), который бьет доступ к встроенным.
ваше решение #2 (с локальным доступом) должно выиграть. Решение #3 имеет медленный точечный поиск (который требует поиска по словарю). Решение № 1 использует нелокальный (вложенная область) доступ, который использует ячейки-переменные (быстрее, чем поиск dict, но медленнее, чем локальные).
Также обратите внимание,KeyError класс исключений является глобальным поиском и может быть ускорен путем его локализации. Вы можете полностью заменить try/except и использовать . И даже это может быть ускорено с помощью метода bound. Конечно, ваш самый простой повышение скорости может просто прийти от пробы pypy : -)
короче, там есть много способов настроить этот код. Просто убедись, что оно того стоит.
в интересах людей, которые натыкаются на этот вопрос, ища способ сделать memoization в python, я рекомендую fastcache.
он работает на python 2 и 3, быстрее, чем любой из описанных выше методов, и дает возможность ограничить размер кэша, чтобы он случайно не стал слишком большим:
from fastcache import clru_cache
@clru_cache(maxsize=128, typed=False)
def foo(cat_1, cat_2, cat_3):
return cat_1 + cat_2 + cat_3
установка fastcache проста, используя pip
:
pip install fastcache
или conda
:
conda install fastcache