2009-10-08 75 views
8

在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事件,以便其他人可以订阅,当他们改变并重置缓存值时得到通知。此方法有效,但有一些缺点:

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

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

+0

即使标记为'delphi',我也很有兴趣知道是否已经开发出特定的模式。 – 2009-10-08 08:41:12

+0

我添加了Delphi标签,以便将建议限制为静态类型,而不是垃圾收集语言。 – Tihauan 2009-10-08 08:47:32

回答

1

在我的工作中,我使用大胆德尔福,可以管理视对方缓存值无限复杂的结构。通常每个变量只包含问题的一小部分。在这个被称为派生属性的框架中。派生的,因为该值没有保存在数据库中,它只依赖于数据库中的其他派生属性或持久属性。

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

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

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

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

编辑: 实际上没有为大胆德尔福为D7下载link for Embarcadero registred users,相当老了......我知道是为D2005,D2006广告更新。

+0

一般的模型驱动框架和“Bold for Delphi”特别听起来很有趣。谢谢! – Tihauan 2009-10-08 20:21:55

+0

我真的在我的一个硬盘上发现了D2006的Bold(我认为它是最新的公开版本),所以如果您有兴趣,只需将邮件发送到r[email protected],我可以发送它。或者通过Skype,我的ID是d98rolb。 – 2009-10-14 14:17:18

+0

Tihauan,欲了解更多信息和一个小例子,Bold的派生值如何工作,请看我的博客http://boldfordelphi.blogspot.com/#derattr。 – 2010-11-14 08:35:11

4

如果你想避免观察者模式,你可以尝试使用哈希方法。

这个想法是你'散列'的参数,并检查它是否匹配'状态保存'的'哈希'。如果没有,那么你重新计算(从而将新散列保存为关键字)。

我知道我听起来像是我刚才想到的,但实际上它是由知名软件使用的。

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

我们已经使用SCons一年多了,我们从未发现任何未重建的目标问题,所以它们的散列效果很好!

+0

只要确保计算散列(或任何您选择的方法)比重新计算(显着)更快。 – 2009-10-08 20:17:26

+0

是的,我没有把它指出来,因为它听起来很明显,但一如既往的优化......你真的必须测量。 – 2009-10-09 08:07:36

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. 
+0

我还会验证fObject1和fObject2是在执行计算之前分配的...为了安全起见。 – skamradt 2009-10-08 17:52:09

+0

@skamradt:同意。我认为这个问题省略了输入验证/错误检测,以保持示例代码的简单性。 – IanH 2009-10-09 08:20:16