2016-04-17 4 views
0

Представьте, что у вас есть функция, которая обрабатывает тяжелое вычислительное задание, которое мы хотим выполнить асинхронно в контексте приложения Tornado. Более того, мы хотели бы лениво оценить функцию, сохранив ее результаты на диске и не повторять функцию дважды для тех же аргументов.Комбинируйте торнадо gen.coroutine и worklib mem.cache decorators

без кэширования результата (запоминание) один будет сделать следующее:

def complex_computation(arguments): 
    ... 
    return result 

@gen.coroutine 
def complex_computation_caller(arguments): 
    ... 
    result = complex_computation(arguments) 
    raise gen.Return(result) 

Предположит, для достижения функции запоминания, мы выбираем памяти класса от joblib. Просто декорирования функцию с @mem.cache функция может быть легко memoized:

@mem.cache 
def complex_computation(arguments): 
    ... 
    return result 

где mem может быть что-то вроде mem = Memory(cachedir=get_cache_dir()).

Теперь рассмотрим объединение двух, где мы выполняем сложную вычислительную функцию исполнителя:

class TaskRunner(object): 
    def __init__(self, loop=None, number_of_workers=1): 
     self.executor = futures.ThreadPoolExecutor(number_of_workers) 
     self.loop = loop or IOLoop.instance() 

    @run_on_executor 
    def run(self, func, *args, **kwargs): 
     return func(*args, **kwargs) 

mem = Memory(cachedir=get_cache_dir()) 
_runner = TaskRunner(1) 

@mem.cache 
def complex_computation(arguments): 
    ... 
    return result 

@gen.coroutine 
def complex_computation_caller(arguments): 
    result = yield _runner.run(complex_computation, arguments) 
    ... 
    raise gen.Return(result) 

Поэтому первый вопрос, является ли вышеупомянутый подход технически правильно?

Теперь давайте рассмотрим следующий сценарий:

@gen.coroutine 
def first_coroutine(arguments): 
    ... 
    result = yield second_coroutine(arguments) 
    raise gen.Return(result) 

@gen.coroutine 
def second_coroutine(arguments): 
    ... 
    result = yield third_coroutine(arguments) 
    raise gen.Return(result) 

Второй вопрос: как можно memoize second_coroutine? Является ли это правильно сделать что-то вроде:

@gen.coroutine 
def first_coroutine(arguments): 
    ... 
    mem = Memory(cachedir=get_cache_dir()) 
    mem_second_coroutine = mem(second_coroutine) 
    result = yield mem_second_coroutine(arguments) 
    raise gen.Return(result) 

@gen.coroutine 
def second_coroutine(arguments): 
    ... 
    result = yield third_coroutine(arguments) 
    raise gen.Return(result) 

[UPDATE I]Caching and reusing a function result in Tornado обсуждает с использованием functools.lru_cache или repoze.lru.lru_cache в качестве решения для второго вопроса.

ответ

1

В Future объекты, возвращенные Торнадо сопрограммам многократного использования, поэтому он обычно работает, чтобы использовать в памяти кэшей, таких как functools.lru_cache, как объяснено в this question. Просто не забудьте положить декоратор кеширования до @gen.coroutine.

На диске кэширование (который, как представляется, вытекает из cachedir аргумента Memory) сложнее, так как Future объекты не могут в общем быть записаны на диск. Ваш пример TaskRunner должен работать, но он делает что-то принципиально отличное от других, потому что complex_calculation не является сопрограммой. Ваш последний пример не будет работать, потому что он пытается поместить объект Future в кеш.

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

def cached_coroutine(f): 
    @gen.coroutine 
    def wrapped(*args): 
     if args in cache: 
      return cache[args] 
     result = yield f(*args) 
     cache[args] = f 
     return result 
    return wrapped 
+0

Спасибо, Бен. Да. Ты прав. Я уже тестировал 'functools.lru_cache' и работает как шарм. Что касается вашего примера, я полагаю, что «кеш» - это что-то, предоставляемое внешним контекстом, поскольку вы, кажется, не определяете что-либо внутри закрытия внешнего декоратора. Тем не менее, ваш пример для меня совершенно ясен, чтобы понять эту идею. Ура! –

 Смежные вопросы

  • Нет связанных вопросов^_^