2009-10-08 7 views
8

In un'applicazione Delphi su cui stiamo lavorando abbiamo una grande struttura di oggetti correlati. Alcune delle proprietà di questi oggetti hanno valori calcolati in fase di esecuzione e sto cercando un modo per memorizzare nella cache i risultati per i calcoli più intensivi. Un approccio che utilizzo è il salvataggio del valore in un membro privato la prima volta che viene calcolato. Ecco un breve esempio:Approcci per i valori calcolati nella cache

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. 

Non è raro che gli oggetti utilizzati per il calcolo cambiamento ed il valore memorizzato nella cache dovrebbe essere azzerato e ricalcolato. Finora abbiamo affrontato questo problema utilizzando il pattern observer: gli oggetti implementano un evento OnChange in modo che altri possano iscriversi, ricevere notifiche quando cambiano e ripristinano i valori memorizzati nella cache. Questo approccio funziona ma presenta alcuni aspetti negativi:

  • La gestione delle sottoscrizioni richiede molta memoria.
  • Non si adatta bene quando un valore memorizzato nella cache dipende da molti oggetti (ad esempio un elenco).
  • La dipendenza non è molto specifica (anche se un valore di cache dipende solo da una proprietà, verrà ripristinato anche quando cambiano altre proprietà).
  • La gestione degli abbonamenti influisce sulle prestazioni generali ed è difficile da mantenere (gli oggetti vengono cancellati, spostati, ...).
  • Non è chiaro come gestire i calcoli in base ad altri valori calcolati.

Infine, la domanda: puoi suggerire altri approcci per l'implementazione dei valori calcolati nella cache?

+0

Anche se taggato "delphi", sono molto interessato a sapere se un particolare modello è stato sviluppato. –

+0

Ho aggiunto il tag Delphi in modo che limiti i suggerimenti ai linguaggi staticamente digitati, non a quelli raccolti. – Tihauan

risposta

1

Nel mio lavoro uso Bold per Delphi in grado di gestire un numero illimitato di complesse strutture di valori memorizzati nella cache a seconda della vicenda. Di solito ogni variabile contiene solo una piccola parte del problema. In questo quadro che si chiama attributi derivati. Derivato perché il valore non è salvato nel database, dipende solo da altri attributi derivati ​​o attributi persistenti nel database.

Il codice dietro tale attributo è scritto in Delphi come procedura o in OCL (Object Constraint Language) nel modello. Se lo scrivi come codice Delphi devi iscriverti alle variabili dipendenti. Quindi se l'attributo C dipende da A e B allora ogni volta che A o B cambia il codice per il ricalcolo C viene chiamato automaticamente quando viene letto C. Quindi la prima volta che viene letto C viene letto anche A e B (forse dal database). Finché A e B non vengono modificati, puoi leggere C e ottenere prestazioni molto veloci. Per calcoli complessi questo può far risparmiare parecchio tempo alla CPU.

Il lato negativo e la cattiva notizia è che Bold non è più supportato ufficialmente e non è possibile acquistarlo. Suppongo che puoi ottenere se chiedi abbastanza persone, ma non so dove puoi scaricarlo. Intorno al 2005-2006 era scaricabile gratuitamente da Borland ma non più. Non è pronto per D2009 poiché qualcuno deve portarlo su Unicode.

Un'altra opzione è ECO con dot.net da Capable Objects. ECO è un plugin in Visual Studio. È un framwork supportato che ha la stessa idea e lo stesso autore di Bold for Delphi. Molte cose sono anche migliorate, ad esempio il databinding è usato per i componenti della GUI. Sia Bold che ECO utilizzano un modello come punto centrale con classi, attributi e collegamenti. Questi possono essere mantenuti in un database o in un file xml. Con la versione gratuita di ECO il modello può avere un massimo di 12 classi, ma come ricordo non ci sono altri limiti.

Grassetto e ECO contengono molto più degli attributi derivati ​​che ti rendono più produttivo e ti permettono di pensare al problema invece dei dettagli tecnici del database o nel tuo caso come memorizzare i valori. Sei il benvenuto con più domande su questi quadri!

Edit: V'è in realtà un download link for Embarcadero registred users per Bold per Delphi per D7, abbastanza vecchio ... So che c'era versioni di D2005, D2006 annuncio.

+0

In generale, i framework basati su modelli e "Bold for Delphi" sono particolarmente interessanti. Grazie! – Tihauan

+0

In realtà ho trovato Bold per D2006 (penso che sia l'ultima versione pubblica) su uno dei miei harddrives, quindi se sei interessato mandami una mail su [email protected] così posso inviarlo. O tramite Skype, il mio id è d98rolb. –

+0

Tihauan, per ulteriori informazioni e un piccolo esempio su come i valori derivati ​​funzionano in grassetto, guarda il mio blog su http://boldfordelphi.blogspot.com/#derattr. –

4

Se si desidera evitare il pattern di osservazione, è possibile provare a utilizzare un approccio di hashing.

L'idea è che si "hash" gli argomenti e si verifica se corrisponde a "hash" per il quale viene salvato lo stato. In caso contrario, si ricalcola (e quindi si salva il nuovo hash come chiave).

So di farlo sembrare come se ci avessi appena pensato, ma in realtà è utilizzato da noti software.

Ad esempio, SCons (alternativa Makefile) consente di verificare se l'obiettivo deve essere ricostruito preferibilmente con un approccio di data/ora.

Abbiamo usato SCons da oltre un anno e non abbiamo mai rilevato alcun problema di target che non è stato ricostruito, quindi il loro hash funziona bene!

+0

Assicurati solo che il calcolo dell'hash (o qualunque metodo tu scelga) sia (significativamente) più veloce del ricalcolo. –

+0

Sì, non l'ho fatto notare perché sembrava ovvio, ma come sempre con l'ottimizzazione ... devi davvero misurare. –

2

È possibile memorizzare copie locali dei valori dell'oggetto esterno necessari. La routine di accesso confronta quindi la copia locale con il valore esterno e esegue solo il ricalcolo su una modifica.

Accedere alle proprietà degli oggetti esterni dovrebbe anche forzare una possibile rivalutazione di tali proprietà, quindi il sistema dovrebbe mantenersi aggiornato automaticamente, ma solo ricalcolare quando è necessario. Non so se è necessario prendere provvedimenti per evitare dipendenze circolari.

Aumenta la quantità di spazio necessario per ciascun oggetto, ma rimuove il modello di osservatore. Inoltre, rimuove tutti i calcoli finché non sono necessari, invece di eseguire il calcolo ogni volta che cambia un parametro di origine. Spero che questo sia rilevante per il tuo sistema.

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

Vorrei anche verificare che fObject1 e fObject2 siano assegnati prima di eseguire il calcolo ... solo per sicurezza. – skamradt

+0

@skamradt: d'accordo. Supponevo che la domanda lasciasse la convalida dell'input/la rilevazione degli errori per mantenere il codice di esempio semplice. – IanH