lib/taskjuggler/DataCache.rb in taskjuggler-3.0.0 vs lib/taskjuggler/DataCache.rb in taskjuggler-3.1.0
- old
+ new
@@ -1,11 +1,11 @@
#!/usr/bin/env ruby -w
# encoding: UTF-8
#
# = DataCache.rb -- The TaskJuggler III Project Management Software
#
-# Copyright (c) 2006, 2007, 2008, 2009, 2010, 2011
+# Copyright (c) 2006, 2007, 2008, 2009, 2010, 2011, 2012
# by Chris Schlaeger <chris@linux.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of version 2 of the GNU General Public License as
# published by the Free Software Foundation.
@@ -18,15 +18,18 @@
# These are the entries in the DataCache. They store a value and an access
# counter. The counter can be read and written externally.
class DataCacheEntry
+ attr_reader :unhashedKey
attr_accessor :hits
- # Create a new DataCacheEntry for the _value_. The access counter is set
- # to 1 to increase the chance that it is not flushed immedidate.
- def initialize(value)
+ # Create a new DataCacheEntry for the _value_. We also store the unhashed
+ # key to be able to detect hash collisions. The access counter is set to 1
+ # to increase the chance that it is not flushed immedidate.
+ def initialize(unhashedKey, value)
+ @unhashedKey = unhashedKey
@value = value
@hits = 1
end
# Return the value and increase the access counter by 1.
@@ -58,10 +61,12 @@
@stores = 0
# Counter for the number of found values.
@hits = 0
# Counter for the number of not found values.
@misses = 0
+ # Counter for hash collisions
+ @collisions = 0
end
# For now, we use this randomly determined size.
def resize(size = 100000)
@highWaterMark = size
@@ -88,40 +93,47 @@
else
# _args_ is a set of arguments that unambigously identify the data entry.
# It's converted into a hash to store or recover a previously stored
- # entry. If we have a value for the key, return the value. Otherwise call the
- # block to compute the value, store it and return it.
+ # entry. If we have a value for the key, return the value. Otherwise call
+ # the block to compute the value, store it and return it.
def cached(*args)
key = args.hash
if @entries.has_key?(key)
e = @entries[key]
- @hits += 1
- e.value
+ if e.unhashedKey != args
+ # Two different args produce the same hash key. This should be a
+ # very rare event!
+ @collisions += 1
+ yield
+ else
+ @hits += 1
+ e.value
+ end
else
@misses += 1
- store(yield, key)
+ store(yield, args, key)
end
end
end
def to_s
<<"EOT"
-Entries: #{@entries.size} Stores: #{@stores}
+Entries: #{@entries.size} Stores: #{@stores} Collisions: #{@collisions}
Hits: #{@hits} Misses: #{@misses}
Hit Rate: #{@hits * 100.0 / (@hits + @misses)}%
EOT
end
private
# Store _value_ into the cache using _key_ to tag it. _key_ must be unique
# and must be used to load the value from the cache again. You cannot
# store nil values!
- def store(value, key)
+ def store(value, unhashedKey, key)
@stores += 1
if @entries.size > @highWaterMark
while @entries.size > @lowWaterMark
# How many entries do we need to delete to get to the low watermark?
@@ -131,10 +143,10 @@
(e.hits -= 1) < 0 && (toDelete -= 1) >= 0
end
end
end
- @entries[key] = DataCacheEntry.new(value)
+ @entries[key] = DataCacheEntry.new(unhashedKey, value)
value
end
end