我写了一些看起来像这样的代码:
def get(x, y)
@cachedResults.set(x,y, Math.hypot(x, y)) if @cachedResults.get(x,y).nil?
@cachedResults.get(x,y)
end
@cachedResults包含我编写的2D Array类(几分钟内),此函数的目的是确保我不必为任何给定的(x,y)调用Math.hypot两次。 [这可以使用对称和其他东西进一步优化,但无论如何]
所以我调用了函数,让它运行160000次;它跑了超过15秒。然后,为了看看它比非Memoized版本快多少,我将代码更改为:
def get(x, y)
Math.hypot(x, y)
end
而且,令我惊讶的是,重新跑了仅需15秒。完全相同的时间。所以我的问题是,ruby中的数学函数自然是Memoized吗?如果是这样,ruby会在多大程度上回忆起来?
(如果没有,那你为什么认为我的结果一致?)
答案 0 :(得分:4)
为你做160000次需要大约15秒吗?只需返回x即可在系统上进行基准测试;很可能是hypot操作(在C中实现)可以忽略解释器开销。
对于使用khell的memoized get方法的ruby 1.8.7,在get方法中调用函数,并在get方法中返回x,并进行100000次迭代:
peregrino:$ time ruby src/memoized_hypot.rb real 0m1.714s user 0m1.436s sys 0m0.080s peregrino:$ time ruby src/plain_hypot.rb real 0m0.495s user 0m0.364s sys 0m0.060s peregrino:$ time ruby src/empty_hypo.rb real 0m0.369s user 0m0.220s sys 0m0.068s
kheill的memoization在每次调用时都会创建一个字符串,这比在每次调用时调用C库的hypot函数要昂贵得多。
调用hypot和刚返回x之间的差异表明hypot仅占运行时的25%。这不是您应该优化的代码 - 而是尝试内联对库的调用,如果您可以而不是将其包装在另一个方法中。
peregrino:$ time ruby src/inline_hypot.rb real 0m0.365s user 0m0.236s sys 0m0.044s
是
100000.times{ |i| Math.hypot(i,6) }
而不是
100000.times{ |i| foo.get(i,6) }
其中foo是发布方法的对象。
这些时间都在上网本(华硕eeepc 900)上,这并不是非常快,所以它们比你的时间快得多,这有点奇怪。所以其他东西可能会支配你的结果。
答案 1 :(得分:3)
试试这个:
def initialize
@cachedResults = {}
end
def get(x, y)
@cachedResults["#{x}:#{y}"] ||= Math.hypot(x, y)
end
答案 2 :(得分:1)
在这种情况下,我不希望这里的记忆会改善很多。
Math.hypot 做的只是sqrt(x**2 + y**2)
。它不是对已经计算的值的递归调用。