2009-10-08 91 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