【问题标题】:Which languages have support for return value caching without boilerplate code?哪些语言支持没有样板代码的返回值缓存?
【发布时间】:2009-05-21 11:02:40
【问题描述】:

对于...的方法

  • 在输入和输出之间存在静态的一对一映射,并且
  • 创建输出对象的成本比较高,而且
  • 使用相同的输入重复调用该方法

...需要缓存结果值。

在我的代码中,以下结果值缓存模式重复了很多次(Java 中的伪代码,但问题与语言无关):

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;
}

一直重复这种结构显然违反了 DRY 原则。

理想情况下,我希望将上面的代码简化为:

@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;
}

理论上的 CacheResult 注释将负责我目前正在手动进行的缓存。

此类缓存的总称是“memoization”。

我正在寻找的确切功能的一个很好的例子是Perl core module "Memoize"

在哪些语言中存在这种类似 Memoize 的缓存解决方案(在语言级别或库级别)?特别是 - 任何主要平台(如 Java 或 .NET)都存在这样的解决方案吗?

【问题讨论】:

  • (请注意,显示的代码会泄漏并且不是线程安全的。)
  • 这是在运行一个 web 上下文,你每次都必须从数据库中获取 Foo 的值?
  • @Chris:不,这个问题与上下文无关。

标签: c# java performance language-agnostic caching


【解决方案1】:

不是内置语言,把 CPAN 模块 Memoize 在 Perl 领域相当流行,我认为:

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

    use Memoize;
    memoize('fib');

【讨论】:

  • 谢谢!这是完全匹配的,实际上它是一个核心 Perl 模块!
【解决方案2】:

缓存处理程序 - 在 .Net '企业库'中

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;
}

【讨论】:

    【解决方案3】:

    Python 有许多装饰器配方,例如decorator module,适用于此(如果参数都是不可变的),它在 JVM 和 .NET 上都有实现。

    【讨论】:

      【解决方案4】:

      Spring 的孵化区,springmodules 正是针对 Java 的这个功能。

      Springmodules cache 仍处于 0.8 版本级别,但当我去年尝试时,它总体上运行良好。有一些选项可以在 spring 配置文件以及注释中配置缓存 - 这看起来与您的示例非常相似。从他们的文档中:

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

      您可以选择缓存的后端实现。当我尝试它时,我将它连接到ehcache 有很好的结果,它也有很好的弹簧集成。您可以以声明方式设置 ehcache 以缓存(在内存中和/或磁盘中)您使用 @Cacheable 注释标记的方法的结果。

      【讨论】:

      • 不带任何Spring配置文件可以使用@Cacheable注解吗?
      【解决方案5】:

      您可以在 Java 中实现 @CacheResult 注释,例如使用 ASMtransform 的方法来添加记忆代码。

      【讨论】:

      【解决方案6】:

      Microsoft T-SQL 可以在 pr 上缓存 CLR 函数的返回值。查询依据...

      (在 CLR 中编写方法时,除了方法上的正确属性外,没有样板。)

      【讨论】:

        【解决方案7】:

        不是直接回答您的问题,但如果您要维护许多缓存,则可能值得使用 OSCache (Java) 来管理这些缓存。淘汰陈旧的对象等,成为您不必担心的问题。

        您仍然必须使用“检查缓存”、“返回缓存”或“创建并添加到缓存”的基本模式。

        【讨论】:

        • 感谢您的回答。我知道 OSCache 和它比 HashMap 的好处,但我提供的代码只是显示机制的伪代码。我不认为 OSCache 提供任何“记忆”功能。
        【解决方案8】:

        尽管 Java 的语法仍然冗长,但可以像在 Java 中那样分解代码

        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);
        }
        

        有了对匿名内部类的更好的句法支持,这可能变成:

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

        这是 AOP 解决方案可以做的一件事,代价是必须处理一些魔法。

        【讨论】:

        • 正如您所指出的,这仍然非常冗长。我在 Perl 或 Python 中寻找更精简的“memoize”。
        • 我不会称它为 非常 在事物的计划中冗长。
        • @knorv:你不能做 perl/python 在 java 中所做的事情的原因是因为在 java 中,函数不是一等公民,所以你不能装饰一个函数。 memoization 过程与它存储的类型紧密耦合,这是 java 中类型系统的结果。不要认为它可以避免。一种自动为你“记忆”的语言是像 haskell 这样的函数式语言 =)
        【解决方案9】:

        This question/answer 解决 C# 中的记忆。它不缓存结果,但可以通过 ReaderWriterLock 轻松更改为静态地图。

        这是来自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;
            };
        }
        

        【讨论】:

          猜你喜欢
          • 2013-09-09
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-03-17
          • 1970-01-01
          • 1970-01-01
          • 2011-01-07
          相关资源
          最近更新 更多