Come memorizzare i dati di stato quando i dati sono correlati a un'altra entità?

1

Per aumentare la capacità parallela dei miei oggetti, cerco di renderli di sola lettura e di includere solo i dati che naturalmente appartengono all'entità. Ho

class Object { ... };
class Processor { 
  void foo(const Object& o) { ... }
}

Il vantaggio di questo modello è che ho bisogno solo di un'istanza Processor per qualsiasi numero di istanze Object . Tuttavia, a volte Processor potrebbe dover memorizzare informazioni di stato per oggetto. Un approccio è quello di memorizzare una mappa all'interno di Processor . Ogni volta che un oggetto viene passato a foo, viene richiesta una ricerca che danneggia le prestazioni.

Un altro approccio è quello di memorizzare le informazioni con l'oggetto. Ma non voglio inquinare l'oggetto con dati che naturalmente non ci appartengono. Inoltre, diversi Processor's memorizzano diversi stati. L'oggetto non può anticipare tutti i tipi di utente.

C'è qualche idioma o trucco di progettazione per questa situazione?

Modifica: Il Processor in questo particolare scenario desidera associare il risultato del calcolo parziale ad ogni oggetto per migliorare la velocità.

    
posta Candy Chiu 29.03.2016 - 18:36
fonte

2 risposte

1

È una cache ( memoization ), quindi progettala come una cache.

One approach is to store a map inside the valuator.

Penso che questo sia l'approccio corretto, o almeno la prima scelta. Qualsiasi altro approccio avrebbe richiesto modifiche o allocazioni aggiuntive con Object . "Mappa" si riferisce al concetto generale che, data una "chiave" estratta da un "Oggetto", si può recuperare un "valore" memorizzato da qualche struttura dati.

Quando usi questo approccio, assicurati che ci sia una robusta "garbage collection" per rimuovere i dati memorizzati nella cache da ogni Processor per gli oggetti che non esistono più. In caso contrario, la cache verrà riempita con dati inutili. Questa è una forma di perdita di memoria / risorse.

In alcune situazioni, potrebbe essere necessario un approccio leggermente ibrido. Considerare il caso in cui non tutti gli oggetti hanno associato i dati memorizzati nella cache. Vorresti evitare una ricerca della mappa hash se non ci sarà. In questo caso, puoi utilizzare un singolo bit (flag) nell'oggetto per indicare l'esistenza di dati memorizzati nella cache in ogni Processor . Questo è un aumento più basso e più prevedibile delle dimensioni della memoria di Object . È conveniente riservare, ad esempio, un limite hard-coded di 64 bit (8 byte) in Object per questo scopo. Per esempio, vedi std::bitset (en.cppreference.com) .

Every time, an object is passed to foo, a look up is required that harms performance.

Risolvi i problemi di prestazioni; non rifiutare questo approccio.

Devi iniziare con ( std::unordered_map en.cppreference.com ). Se in precedenza hai utilizzato std::map , puoi passare a std::unordered_map e non devi preoccuparti del problema di prestazioni.

Se std::unordered_map non è abbastanza veloce, vorrai conoscere a fondo l'implementazione sottostante che stai utilizzando. Verifica se una ricerca è O(1) o O(log N) . Controlla la funzione hash. Controlla i tassi di collisione. Controlla come gestisce la collisione. Verifica i criteri che utilizza per crescere.

Dovrai imparare a leggere il codice di disassemblaggio compilato e utilizzare gli strumenti di profilatura della CPU (più correttamente campionamento della CPU ) per verificare se la mappa hash si qualifica come hotspot e merita le ottimizzazioni delle prestazioni Sei disposto a investire.

Se sei preoccupato per i problemi di localizzazione della cache della CPU, tieni presente quanto segue:

  • Se gli oggetti Object sono disposti sequenzialmente in memoria e sono accessibili in sequenza, e Processor visita anche oggetti nello stesso ordine sequenziale, puoi progettare la cache per disporre i dati memorizzati nella cache nello stesso ordine sequenziale come i tuoi oggetti. Qui è utile la personalizzazione della mappa di hash.
  • Se uno dei precedenti non è vero, cioè non tutto è ordinato sequenzialmente, l'efficienza della cache sarà ridotta a "ciò che è popolare" (oggetti visitati più frequentemente di altri) e "ciò che è recente". Questo è il livello base di prestazioni che puoi aspettarti; non devi fare alcuna ottimizzazione del codice per raggiungere questo livello di prestazioni. Ottimizza solo all'interno di ciascun oggetto.
risposta data 28.05.2016 - 23:12
fonte
1

Sembra che tu stia cercando di creare il tuo codice in base ad alcuni dei principi del programmazione funzionale paradigma:

  • oggetti / dati sono immutable : come te nota che rende facile l'elaborazione degli oggetti / dati in parallelo.
  • no lato effetti : le operazioni eseguite su un oggetto / dati non dovrebbero influire su questo stato oggetto / dati (o qualsiasi interazione con le funzioni di chiamata o il mondo esterno.

La soluzione nel mondo della programmazione funzionale può essere piuttosto semplice:
crea un nuovo oggetto basato su quello originale con lo stato per quel Processor .

Il riferimento all'oggetto / dati originale nella coda (o altro meccanismo) per quel Processor dovrebbe essere sostituito con un riferimento al nuovo oggetto / dati.
 L'oggetto / dati originali esiste ancora per l'altro Processors (non viene cancellato o sovrascritto).

Questo porta a un sovraccarico in memoria (esistono più versioni dello stesso oggetto con stati diversi), ma non dovrebbe danneggiare le prestazioni.

    
risposta data 29.03.2016 - 21:05
fonte

Leggi altre domande sui tag