2008年7月中旬,Memoization被添加到Rails核心.这里使用的演示.
我还没有找到任何关于何时应该记忆方法的好例子,以及每个方法的性能影响.例如,这篇博客文章表明,通常不应该使用备忘录.
对于可能具有巨大性能影响的东西,除了提供简单的教程外,似乎没有什么资源可供使用.
有没有人看到他们自己的项目中使用的memoization?哪些因素会让您考虑记忆方法?
在我自己做了一些研究之后,我发现在Rails核心中使用了很多次memoization.
这是一个例子:http://github.com/rails/rails/blob/1182658e767d2db4a46faed35f0b1075c5dd9a88/actionpack/lib/action_view/template.rb.
这种用法似乎违背了上述博客文章的发现,发现备忘可能会损害性能.
我认为许多Rails开发人员并不完全了解memoization的作用及其工作原理.我已经看到它应用于返回延迟加载集合的方法(如Sequel数据集),或应用于不带参数但基于实例变量计算某些东西的方法.在第一种情况下,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应用于该函数.
当一个方法从多个表中获取数据,并在返回结果对象之前执行某些计算,并且此方法在请求中多次执行时,memoization可能有意义.
请记住,查询缓存也是活动的,因此只有memoize方法执行in-Ruby计算,而不是纯数据库提取.