in

DotNetSide

Dot Net South Italy Developers User Group

Tips

StringBuilder vs Concatenamento

 

Autore: Michele Locuratolo

Mi capita spesso che mi venga richiesto come fare per migliorare le performance di una applicazione e, altrettanto spesso, scopro che viene usato il semplice concatenamento di stringhe per comporre dei messaggi. Si va dal semplice messaggio di poche parole (che concatena 4 o 5 stringhe) a veri e propri testi.
Questo modo di fare causa spesso problemi di performance. Scopriamo insieme il perchè.
Usiamo allo scopo questo semplice codice:

 string myString = string.Empty;
 
for (int i = 0; i < 10000; i++) {
     myString += i.ToString();
 }

Il funzionamento è estremamente semplice: la nostra stringa sarà il risultato del concatenamento di 10.000 numeri.
Internamente però, questa operazione è decisamente pesante. Partendo dal concetto che una stringa è un oggetto immutabile e che, ad ogni modifica di essa, ne deve essere creata una copia che contiene il nuovo valore, non è difficile immaginare cosa accade all'interno del nostro ciclo.
Al primo "giro", verrà creata una nuova stringa che conterrà il valore precedente (string.Empty) ed il nuovo (0). Al secondo cliclo, ancora una volta, verrà creata una nuova stringa che contenente il valore precedente ed il nuovo (1) e così via. Ne consegue che, per 10.000 cicli, verranno create 10.000 stringhe! Tutte le stringhe precedenti vengono poi eliminate dal Garbage Collector.
Se mandiamo in esecuzione il codice di sopra aggiungendo un paio di timer, otterremo il seguente risultato:

Avvio ciclo di concatenamento
Tempo impiegato: 00:00:00.5107344

Praticamente 1/2 secondo per concatenare 10.000 stringhe. Apparentemente potrebbe sembrare poco ma vediamo cosa accade usando un oggetto realizzato apposta per questo scopo: lo StringBuilder:

 StringBuilder sb = new StringBuilder(10000);
 
for (int i = 0; i < 10000; i++) {
     sb.Append(i.ToString());
 }

Grazie a questo oggetto, il concatenamento non avviene più creando una copia della stringa. StringBuilder contiene un vettore di caratteri ed il concatenamento avviene direttamente sul vettore evitando inutili copie di oggetti. Se mandiamo in esecuzione il codice otterremo:

Avvio ciclo con StringBuilder
Tempo impiegato: 00:00:00.0100144

50 volte più veloce! Ma la velocità è solo la minima conseguenza. Se usiamo un tool come CLR Profiler (esiste sia per la versione 1.1 che 2.0 del framework), possiamo vedere quanto sia più pesante da eseguire il primo codice rispetto al secondo. Di seguito un report:

  Concatenamento StringBuilder
Allocated bytes:
Relocated bytes:
Final Heap bytes:
Objects finalized:
Critical objects finalized:
Gen 0 collections:
Gen 1 collections:
Gen 2 collections:
Induced collections:
Gen 0 Heap bytes:
Gen 1 Heap bytes:
Gen 2 Heap bytes:
Large Object Heap bytes:
Handles created:
Handles destroyed:
Handles surviving:
Heap Dumps:
Comments:

379.311.360
20.536.640
1.575.328
0
0
449
47
0
0
847.516
390.678
12
8.784
27
0
27
0
0

544.756
0
544.756
0
0
0
0
0
0
Unknown
Unknown
Unknown
Unknown
27
0
27
0
0

Come è evidente, tanto la memoria allocata quanto il lavoro fatto dal Garbage Collector sono notevolmente ridotti dallo StringBuilder.
Personalmente, se devo concatenare più di 3 stringhe, uso il secondo codice. Fino a 3, non c'è quasi differenza tra i 2 sistemi.

Only published comments... Jun 05 2006, 01:17 AM by VitoA
Filed under:
Powered by Community Server (Commercial Edition), by Telligent Systems