Когда использовать памятку в Ruby on Rails - PullRequest
23 голосов
/ 30 марта 2009

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

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

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

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


Проведя собственное исследование, я обнаружил, что в ядре Rails много раз используется памятование.

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

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

Ответы [ 3 ]

33 голосов
/ 04 декабря 2009

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

Я бы не применил бы памятку, если

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

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

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

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 не имеет или полагается на какие-либо побочные эффекты)

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

10 голосов
/ 30 марта 2009

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

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

2 голосов
/ 05 апреля 2011

Возможно, мой опыт - хороший пример того, когда НЕ использовать памятку. В моей модели Order я запомнил оба простых результата расчета, то есть промежуточный итог Order #, Order # tax; а также объекты модели, т.е. Order # most_recent_credit_card_used. В последнем случае, когда я запоминаю метод, который возвращает объект CreditCard, я получаю ошибки «замороженного хеша» при попытке обновить атрибуты запомнившегося объекта. Заказ # most_recent_credit_card_used.frozen? вернул true, когда метод был запомнен, что, конечно, не то, что я хотел.

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

...