高效的Ruby LRU缓存

时间:2009-12-19 19:12:53

标签: ruby caching

使用任意Ruby对象构建缓存的最有效方法是基于最近最少使用的算法过期的密钥。它应该使用Ruby的普通散列语义(不等于?)

8 个答案:

答案 0 :(得分:27)

我知道它已经晚了几年,但我刚刚为Ruby实现了我认为最快的LRU Cache。

它还经过测试,可选择安全地在多线程环境中使用。

https://github.com/SamSaffron/lru_redux


注意:在Ruby 1.9中,Hash是有序的,所以你可以在几行代码中欺骗和构建最快的LRU缓存

class LruRedux::Cache19

  def initialize(max_size)
    @max_size = max_size
    @data = {}
  end

  def max_size=(size)
    raise ArgumentError.new(:max_size) if @max_size < 1
    @max_size = size
    if @max_size < @data.size
      @data.keys[0..@max_size-@data.size].each do |k|
        @data.delete(k)
      end
    end
  end

  def [](key)
    found = true
    value = @data.delete(key){ found = false }
    if found
      @data[key] = value
    else
      nil
    end
  end

  def []=(key,val)
    @data.delete(key)
    @data[key] = val
    if @data.length > @max_size
      @data.delete(@data.first[0])
    end
    val
  end

  def each
    @data.reverse.each do |pair|
      yield pair
    end
  end

  # used further up the chain, non thread safe each
  alias_method :each_unsafe, :each

  def to_a
    @data.to_a.reverse
  end

  def delete(k)
    @data.delete(k)
  end

  def clear
    @data.clear
  end

  def count
    @data.count
  end

  # for cache validation only, ensures all is sound
  def valid?
    true
  end
end

答案 1 :(得分:10)

这推动了我对Ruby如何使用内存的理解的界限,但我怀疑最有效的实现将是一个双向链接列表,其中每个访问都将密钥移动到列表的前面,并且每个插入都会丢弃一个项目如果已达到最大尺寸。

然而,假设Ruby的Hash类已经非常有效,我敢打赌,简单地将年龄数据添加到Hash这个有点天真的解决方案会相当不错。这是一个快速的玩具示例:

class Cache
  attr_accessor :max_size

  def initialize(max_size = 4)
    @data = {}
    @max_size = max_size
  end

  def store(key, value)
    @data.store key, [0, value]
    age_keys
    prune
  end

  def read(key)
    if value = @data[key]
      renew(key)
      age_keys
    end
    value
  end

  private # -------------------------------

  def renew(key)
    @data[key][0] = 0
  end

  def delete_oldest
    m = @data.values.map{ |v| v[0] }.max
    @data.reject!{ |k,v| v[0] == m }
  end

  def age_keys
    @data.each{ |k,v| @data[k][0] += 1 }
  end

  def prune
    delete_oldest if @data.size > @max_size
  end
end

找到最老的项目可能有一种更快的方法,而且还没有经过全面测试,但我很想知道有人认为这与更复杂的设计,链接列表或其他方式相比。

答案 2 :(得分:4)

Remaze拥有经过充分考验的LRU缓存:请参阅http://github.com/manveru/ramaze/blob/master/lib/ramaze/snippets/ramaze/lru_hash.rb

rubyworks还有hashery gem,它应该比大缓存的重映效率更高。

答案 3 :(得分:2)

rufus-lru gem是另一种选择。

它不是计数,而是保存从最旧到最新的键的排序数组

答案 4 :(得分:2)

我把一个你可能觉得有用的新宝石lrucache扔了起来。对于具有大量元素的集合,它可能比Alex的方法更快。

答案 5 :(得分:1)

Ruby Best Practices博客有一个post

答案 6 :(得分:1)

答案 7 :(得分:0)

gem install ruby-cache

- &GT; http://www.nongnu.org/pupa/ruby-cache-README.html