views:

365

answers:

11

For methods where ...

  • there exists a static one-to-one mapping between the input and the output, and
  • the cost of creating the output object is relatively high, and
  • the method is called repeatedly with the same input

... there is a need for caching result values.

In my code the following result value caching pattern is repeated a lot (pseudo-code in Java, but the question is language-agnostic):

private static Map<Input, Output> fooResultMap = new HashMap<Input, Output>();
public getFoo(Input input) {
  if (fooResultMap.get(input) != null) {
    return fooResultMap.get(input);
  }
  Output output = null;
  // Some code to obtain the object since we don't have it in the cache.
  fooResultMap.put(input, output);
  return output;
}

Repeating this structure all the time is a clear violation of the DRY principle.

Ideally, I'd like the code above to be reduced to the following:

@CacheResult
public getFoo(Input input) {
  Output output = null;
  // Some code to obtain the object since we don't have it in the cache.
  return output;
}

Where the theoretical CacheResult annotation would take care of the caching I'm currently doing by hand.

The general term for this type of caching is "memoization".

A good example of the exact functionality I'm looking for is Perl core module "Memoize".

In which languages does such a Memoize-like caching solution exist (either at the language level or the library level)? In particular - does such a solution exist for any major platform such as Java or .NET?

+1  A: 

Python has a number of decorator recipes, e.g. the decorator module, that work for this (if the parameters are all immutable), and it has implementations on both the JVM and .NET.

Hank Gay
A: 

There are a lot of third-party caching mechanisms for java, of which some of them do something similar to what you are doing...

you can check for example this website

You can also write your own, more generic solution for this problem, so you only have one hashtable and an object which handles the caching for multiple object types...

Fortega
Please re-read the question. Your answer fails to address it.
knorv
+4  A: 

Not a language built-in, put the CPAN module Memoize is reasonably popular in Perl land, I think:

   # Compute Fibonacci numbers
    sub fib {
      my $n = shift;
      return $n if $n < 2;
      fib($n-1) + fib($n-2);
    }

    use Memoize;
    memoize('fib');
Thilo
Thanks! That's an exact match, and actually it's a core Perl module!
knorv
A: 

Not a direct answer to your question, but if you are maintaining many caches, it may be worthwhile to use OSCache (Java) for management of those caches. Eviction of stale objects etc, becomes a problem you don't have to worry about.

You would still have to use the basic pattern of "check cache", "return cached" or "create and add to cache" though.

Harry Lime
Thanks for your answer. I'm aware of OSCache and it's benefits over HashMap, but the code I provided is just pseudo-code to show the mechanism. I don't think OSCache provides any "memoize" functionality.
knorv
A: 

It is possible to factor out code like that in Java, although Java's syntax remains verbose

private static final Cache<Input, Output> fooCache = Caches.newInstance(
    new Factory<Input, Output>() { public Output create(Input input) {
        return ... some code ...;
    }}
);
public static Output getFoo(Input input) {
    return fooCache.get(input);
}

With better syntactical support for anonymous inner classes, that could become, say:

private static final Cache<Input, Output> fooCache =
    (Input input) (... some code ...);
public static Output getFoo(Input input) {
    return fooCache.get(input);
}

This is one thing that AOP solution can do, at the expense of having to deal with a bit of magic.

Tom Hawtin - tackline
As you point out that's still very verbose. I'm looking for something more lean à la "memoize" in Perl or Python.
knorv
I wouldn't call it *very* verbose in the scheme of things.
Tom Hawtin - tackline
@knorv: the reason you cannot do what perl/python does in java is because in java, functions are not first class citizens, and so you cannot decorate a function. The memoization process is tightly coupled to the type it is storing, and that is the result of the type system in java. dont think it can be avoided. A language that automatically "memoizes" for you is a functional language like haskell =)
Chii
A: 

You could implement the @CacheResult annotation in Java, using for example ASM to transform the method to add the memoization code.

Pete Kirkham
This: http://www.tek271.com/?about=software/java/memoizer/tek271.memoizer.intro.html seems like it's actually doing this for java
Toader Mihai Claudiu
A: 

This question/answer addresses Memoization in C#. It doesn't cache the results, but could be easily changed to make the map static with a ReaderWriterLock.

Here's a sample from the link given:

public static Func<A, R> Memoize<A, R>(this Func<A, R> f)
{
  var map = new Dictionary<A, R>();
  return a =>
    {
      R value;
      if (map.TryGetValue(a, out value))
        return value;
      value = f(a);
      map.Add(a, value);
      return value;
    };
}
Chris S
A: 

A simple C++ implementation wouls look something like thsi:

template <typename Input, typename Output>
const Output & getCached( Input & input) {
  static Output * cached = 0;
  // Input must implement operator () which returns new Output
  if ( cached == 0 ) {
     cached = input();
  }
  return * cached;
}
anon
A: 

Microsoft T-SQL can cache the return values from a CLR function on a pr. query basis...

(No boilerplate except the correct attributes on the method when writing it in CLR.)

Arjan Einbu
+1  A: 

The Spring's incubation area, springmodules has exactly this functionality for Java.

Springmodules cache is still at 0.8 release level, but it worked generally quite well when I tried it last year. There are options to configure the caching within spring configuration files as well as with annotations - which looks pretty much exactly like your example. From their docs:

public class TigerCacheableService implements CacheableService {

  @Cacheable(modelId = "testCaching")
  public final String getName(int index) {
    // some implementation.
  }
...
}

You can choose the back end implementation of the cache. When I was trying it I had good results hooking it up to ehcache which has nice spring integration too. You can declaratively set up ehcache to cache (in-memory and/or disk) the results of the methods that you tag with the @Cacheable annotation.

serg10
+2  A: 

The Caching Handler - in .Net 'Enterprise Library'

http://msdn.microsoft.com/en-us/library/cc511757.aspx

[CachingCallHandler(0, 0, 30)]
public decimal GetSavingsBalance(int accountNumber)
{
  // Code here to extract balance from database.
  return balance;
}
Oli