缓存计算值的方法

时间:2009-10-08 08:14:25

标签: delphi oop caching delphi-2006

在我们正在开发的Delphi应用程序中,我们有一个相关对象的大结构。这些对象的某些属性具有在运行时计算的值,我正在寻找一种方法来缓存更密集计算的结果。我使用的方法是在第一次计算私有成员时保存值。这是一个简短的例子:

unit Unit1;

interface

type
  TMyObject = class
  private
    FObject1, FObject2: TMyOtherObject;
    FMyCalculatedValue: Integer;
      function GetMyCalculatedValue: Integer;
  public
    property MyCalculatedValue: Integer read GetMyCalculatedValue;
  end;

implementation

  function TMyObject.GetMyCalculatedValue: Integer;
  begin
    if FMyCalculatedValue = 0 then
    begin
      FMyCalculatedValue :=
        FObject1.OtherCalculatedValue + // This is also calculated
        FObject2.OtherValue;
    end;

    Result := FMyCalculatedValue;
  end;

end.

用于计算的对象更改并且应重置和重新计算缓存的值并不罕见。到目前为止,我们通过使用观察者模式解决了这个问题:对象实现OnChange事件,以便其他人可以订阅,在更改和重置缓存值时得到通知。这种方法有效但有一些缺点:

  • 管理订阅需要大量内存。
  • 当缓存值取决于大量对象(例如列表)时,它不能很好地扩展。
  • 依赖关系不是非常具体(即使缓存值仅依赖于一个属性,当其他属性发生更改时,它也会被重置)。
  • 管理订阅会影响整体性能并且难以维护(对象被删除,移动,......)。
  • 目前尚不清楚如何根据其他计算值处理计算。

最后一个问题:你能否提出其他方法来实现缓存的计算值?

3 个答案:

答案 0 :(得分:4)

如果要避免观察者模式,可以尝试使用散列方法。

这个想法是你'散列'参数,并检查这是否与保存状态的'hash'匹配。如果没有,则重新计算(从而将新哈希保存为密钥)。

我知道我听起来像是在考虑它,但事实上它被知名软件使用。

例如,SCons(Makefile替代)用于检查目标是否需要优先重新构建为时间戳方法。

我们已经使用SCons一年多了,我们从未发现任何未重建的目标问题,因此它们的哈希效果很好!

答案 1 :(得分:2)

您可以存储所需的外部对象值的本地副本。然后,访问例程将本地副本与外部值进行比较,并仅对更改进行重新计算。

访问外部对象属性同样会强制重新评估这些属性,因此系统应自动保持最新状态,但只在需要时重新计算。我不知道你是否需要采取措施来避免循环依赖。

这会增加每个对象所需的空间量,但会删除观察者模式。它还会延迟所有计算,直到需要它们,而不是每次源参数更改时执行计算。我希望这与您的系统相关。

unit Unit1;

interface

type
  TMyObject = class
  private
    FObject1, FObject2: TMyOtherObject;
    FObject1Val, FObject2Val: Integer;
    FMyCalculatedValue: Integer;
      function GetMyCalculatedValue: Integer;
  public
    property MyCalculatedValue: Integer read GetMyCalculatedValue;
  end;

implementation

  function TMyObject.GetMyCalculatedValue: Integer;
  begin
    if (FObject1.OtherCalculatedValue <> FObjectVal1)
    or (FObject2.OtherValue <> FObjectVal2) then
    begin
      FMyCalculatedValue :=
        FObject1.OtherCalculatedValue + // This is also calculated
        FObject2.OtherValue;
      FObjectVal1 := FObject1.OtherCalculatedValue;
      FObjectVal2 := Object2.OtherValue;
    end;

    Result := FMyCalculatedValue;
  end;

end.

答案 2 :(得分:1)

在我的工作中,我使用 Bold for Delphi ,可以根据彼此管理无限缓存值的复杂结构。通常每个变量只占问题的一小部分。在此框架中称为派生属性。派生是因为该值未保存在数据库中,它仅取决于数据库中的其他派生属性或持久属性。

这种属性背后的代码在Delphi中作为过程或在模型中的OCL(对象约束语言)中编写。如果你把它写成Delphi代码,你必须订阅依赖变量。因此,如果属性C依赖于A和B,则每当A或B更改用于重新计算的代码时,在读取C时将自动调用。所以第一次读取C和A也被读取(可能来自数据库)。只要A和B没有改变,你就可以阅读C并获得非常快的性能。对于复杂的计算,这可以节省大量的CPU时间。

缺点和坏消息是Bold不再受到官方支持,你也买不到它。我想你可以得到足够的人,但我不知道你可以在哪里下载它。在2005-2006左右,它可以从Borland免费下载,但现在不能再下载了。 它没有为D2009做好准备,因为有人必须将它移植到Unicode。

另一个选项ECO来自Capable Objects的dot.net。 ECO是Visual Studio中的插件。这是一个支持的框架,与Delphi的Bold具有相同的想法和作者。许多事情也得到了改进,例如数据绑定用于GUI组件。 Bold和ECO都使用模型作为具有类,属性和链接的中心点。这些可以保存在数据库或xml文件中。使用免费版ECO,该模型最多可以有12个类,但我记得没有其他限制。

粗体和ECO包含的内容远多于派生属性,这些属性可以提高您的工作效率,让您可以考虑问题而不是数据库的技术细节,或者在您的情况下如何缓存值。欢迎您提出有关这些框架的更多问题!

修改 实际上有一个下载link for Embarcadero registred users用于D7的 Bold for Delphi ,很老......我知道有D2005的更新,广告D2006。