2013-05-19 12 views
6

Attualmente sto lavorando su un'applicazione legacy molto grande che gestisce una grande quantità di dati stringa raccolti da varie fonti (IE, nomi, identificatori, codici comuni relativi al business, ecc.). Questi dati da soli possono richiedere fino a 200 meg di ram nel processo di applicazione.Memorizzazione nella cache. Ottimizzazione della memoria e riutilizzo

Un mio collega ha menzionato una possibile strategia per ridurre l'impronta di memoria (dato che molte stringhe individuali sono duplicate tra i set di dati), sarebbe quella di "memorizzare" le stringhe ricorrenti in un dizionario e riutilizzarle Quando richiesto. Così, per esempio ...

public class StringCacher() 
{ 
    public readonly Dictionary<string, string> _stringCache; 

    public StringCacher() 
    { 
     _stringCache = new Dictionary<string, string>(); 
    } 

    public string AddOrReuse(string stringToCache) 
    { 
     if (_stringCache.ContainsKey(stringToCache) 
      _stringCache[stringToCache] = stringToCache; 

     return _stringCache[stringToCache]; 
    } 
} 

quindi di utilizzare questo caching ...

public IEnumerable<string> IncomingData() 
{ 
    var stringCache = new StringCacher(); 

    var dataList = new List<string>(); 

    // Add the data, a fair amount of the strings will be the same. 
    dataList.Add(stringCache.AddOrReuse("AAAA")); 
    dataList.Add(stringCache.AddOrReuse("BBBB")); 
    dataList.Add(stringCache.AddOrReuse("AAAA")); 
    dataList.Add(stringCache.AddOrReuse("CCCC")); 
    dataList.Add(stringCache.AddOrReuse("AAAA")); 

    return dataList; 
} 

come stringhe sono immutabili e un sacco di lavoro interno è fatto da quadro per farli lavorare in modo simile a tipi di valore Sto pensando a metà che questo creerà solo una copia di ciascuna stringa nel dizionario e raddoppierà solo la quantità di memoria utilizzata anziché passare semplicemente un riferimento alla stringa memorizzata nel dizionario (che è ciò che il mio collega sta assumendo).

Quindi tenendo conto che questo sarà eseguito su una serie massiccia di dati di stringa ...

  • è questa intenzione di salvare qualsiasi memoria, partendo dal presupposto che il 30% dei valori di stringa verrà usata due volte o più?

  • L'ipotesi è che funzioni correttamente?

+2

Questo è un errore, il 30% non è abbastanza per giustificare la realizzazione del programma un centinaio di volte più lento. La RAM è economica e abbondante, 8 gigabyte costano 67 dollari. Non è possibile scrivere una riga di codice per $ 1,64 –

+0

+1 su @HansPassant per calcolare il tempo rispetto alla ROI della RAM. –

+0

@ HansPassant Grazie per aver segnalato questo. Mi assicurerò di eseguire test delle prestazioni durante l'implementazione. Sono d'accordo, quella memoria nel tuo PC medio è sporca a buon mercato in questi giorni, ma sfortunatamente quando si parla di workstation di produzione in un grande istituto finanziario, dove tutta la memoria (e qualsiasi altra parte) deve essere acquistata e installata attraverso un fornitore specifico, spinge il costo reale di 8 gig a oltre 500 dollari per workstation. Moltiplicalo per più di 1000 utenti e puoi capire perché gli aggiornamenti delle macchine non sono realmente un'opzione. – Moog

risposta

7

Questo è essenzialmente ciò che lo string interning è, tranne che non devi preoccuparti di come funziona. Nell'esempio si sta ancora creando una stringa, quindi confrontandola, quindi lasciando la copia da smaltire. .NET lo farà per te in runtime.

Vedi anche String.Intern e Optimizing C# String Performance (C Calvert)

Se una nuova stringa viene creato con il codice simile (String goober1 = "foo"; String goober2 = "foo";) mostrata in linee 18 e 19, quindi la tabella interna viene controllata. Se la stringa è già presente, entrambe le variabili punteranno allo stesso blocco di memoria gestito dalla tabella interna.

Quindi, non è necessario eseguire il rollover - non fornirà alcun vantaggio. EDIT UNLESS: le stringhe in genere non vivono fino a quando le stringhe internate di AppDomain sono attive per tutta la vita di AppDomain, il che non è necessariamente ottimo per GC. Se vuoi stringhe di breve durata, allora vuoi una piscina. Da String.Intern:

Se si sta tentando di ridurre la quantità totale di memoria allocata dall'applicazione, tenere presente che l'interning di una stringa ha due effetti collaterali indesiderati. Innanzitutto, la memoria allocata per gli oggetti String interni non verrà rilasciata finché il Common Language Runtime (CLR) non termina. Il motivo è che il riferimento CLR dell'oggetto String internato può persistere dopo la chiusura dell'applicazione o persino del dominio dell'applicazione. ...

EDIT 2 Vedere anche Jon Skeets SO answer here

+0

Probabilmente non ci sarà un buon set di dati per tutta la vita dell'applicazione, quindi forse in questo caso sarebbe più efficiente per memorizzarli in un dizionario che posso cancellare quando i set di dati non sono più necessari. – Moog

+0

Sembra ragionevole. Lo internamento di stringhe è perfetto per letterali e costanti definiti nel codice, per stringhe di localizzazione che occupano uno spazio significativo e possono beneficiare di "deduplicazione" per le app di stile [CMS] (https://en.wikipedia.org/wiki/Content_management_system) che mantenere le stringhe in memoria. Ma se si è, ad esempio, tirando giù il codice html da un server web, elaborando le sezioni da esso e poi gettandole via tutte, si potrebbe star meglio con il proprio pool di deduplicazione. –

+0

@Moog, nota anche: '_stringCache [stringToCache] = stringToCache;' come hai scritto potrebbe duplicare quella stringa (una volta per la chiave, una volta per il valore), non sono sicuro perché sto esaurendo il porta - ma vale la pena controllare. –

3

Questo è già built-in .NET, si chiama String.Intern, non c'è bisogno di reinventare.

+0

OK, bello, non lo sapevo !! Quindi utilizzare questo metodo per memorizzare le stringhe avrebbe un effetto notevole sull'impronta di memoria? Inciderebbe molto sulle prestazioni se si chiama String.Intern su centinaia di migliaia di stringhe? – Moog

+0

Dovrebbe ridurre il consumo di memoria e migliorare le prestazioni. Devi testarlo per essere in grado di vedere l'impatto sulla tua applicazione. – oleksii

+2

@Moog. Attenzione però: le stringhe internate vivono per tutta la vita dell'AppDomain, quindi non sono GC. Se vuoi stringhe di breve durata l'idea del tuo pool potrebbe essere migliore (vedi il mio commento nella mia risposta) –

2

È possibile raggiungere questo obiettivo con il costruito nel funzionalità .Net.

Quando si inizializza la stringa, effettuare una chiamata a string.Intern() con la stringa.

Ad esempio:

dataList.Add(string.Intern("AAAA")); 

Ogni chiamata successiva con la stessa stringa utilizzerà lo stesso riferimento in memoria. Quindi se hai 1000 AAAA, solo 1 copia di AAAA viene memorizzata.

Problemi correlati