Подтвердить что ты не робот

Когда использовать memoization в Ruby on Rails

В середине июля 2008 года в ядро ​​Rails было добавлено Memoization. Демонстрация использования здесь.

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

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

Кто-нибудь видел воспоминания, используемые в их собственных проектах? Какие факторы заставят вас подумать о мемонировании метода?


Сделав еще несколько исследований, я обнаружил, что memoization используется замечательное количество раз внутри ядра Rails.

Вот пример: http://github.com/rails/rails/blob/1182658e767d2db4a46faed35f0b1075c5dd9a88/actionpack/lib/action_view/template.rb.

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

4b9b3361

Ответ 1

Я думаю, что многие разработчики Rails не совсем понимают, что делает memoization и как это работает. Я видел, как он применялся к методам, возвращающим ленивые загруженные коллекции (например, пакет данных Sequel), или применяется к методам, которые не принимают аргументов, а вычисляют что-то на основе переменных экземпляра. В первом случае memoization - это не что иное, как накладные расходы, а во втором - это источник неприятных и трудноотслеживаемых ошибок.

Я бы не применял memoization, если

  • возвращаемое значение просто немного дорого для вычисления. Это должно было быть очень дорогостоящим и не оптимизироваться, поскольку оно заслуживает воспоминания.
  • возвращаемое значение может быть или может быть ленивым.
  • метод не является чистой функцией, то есть гарантированно возвращает точно такое же значение для одних и тех же аргументов - и использует только аргументы для его работы или другие чистые функции. Использование переменных экземпляра или методов вызова, которые в свою очередь используют переменные экземпляра, означает, что метод может возвращать разные результаты для тех же аргументов.

Есть и другие ситуации, когда memoization не подходит, например, вопрос в вопросе и ответы выше, но это три, которые, я думаю, не так очевидны.

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

def unmemoizable1(name)
  "%s was here %s" % name, Time.now.strftime('%Y-%m-%d')
end

def unmemoizable2
  find_by_shoe_size(@size)
end

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

def unmemoizable1(name)
  memoizable1(name, Time.now.strftime('%Y-%m-%d'))
end

def memoizable1(name, time)
  "#{name} was here #{time}"
end
memoize :memoizable1

def unmemoizable2
  memoizable2(@size)
end

def memoizable2(size)
  find_by_shoe_size(size)
end
memoize :memoizable2

(предполагая, что find_by_shoe_size не имеет или не полагается на какие-либо побочные эффекты)

Хитрость состоит в том, чтобы извлечь чистую функцию из метода и применить вместо нее memoization.

Ответ 2

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

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

Ответ 3

Возможно, мой опыт - хороший пример того, когда НЕ использовать memoize. В моей модели заказа я помнил как простые результаты расчета, т.е. Подзаголовок заказа #, налог на заказ #; а также объекты модели, т.е. порядок # most_recent_credit_card_used. В последнем случае, когда memoizing метод, который возвращает объект CreditCard, я получал бы "замороженные хэш-ошибки" при попытке обновить атрибуты memoized объекта. Заказ # most_recent_credit_card_used.frozen? true, когда метод был замечен, что, конечно, не то, что я намеревался.

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