Che è più veloce: raccolta chiara o nuova istanza

Ho un certo numero di liste generiche nel mio codice, che hanno decine o centinaia di elementi. A volte ho bisogno di riempire queste liste con altri oggetti, quindi la domanda è: cosa sarà più veloce, chiamare il metodo Clear() o creare una new List() ?

cosa sarà più veloce, chiamare il metodo Clear() o creare un `new List ()?

È imansible rispondere. Dipende davvero da molti fattori, tra cui per quanto tempo la collezione è esistita.

L’opzione migliore qui sarebbe:

  1. Profili l’applicazione e vedi se ciò è veramente importante. Probabilmente non farà alcuna differenza percepibile, nel qual caso userò il metodo che ha più senso in termini di come pensi a questo object.

  2. Se è importante, scrivi entrambi i set di codici e misura la differenza di velocità (se presente).

Da un punto di vista pratico, chiamare Clear() non ridurrà effettivamente la memoria (utilizzata dallo stesso List ), poiché non riduce la capacità dell’elenco, elimina solo i valori contenuti al suo interno. La creazione di un nuovo List causerà l’allocazione di un nuovo elenco, che a sua volta causerà più allocazioni con crescita.

Ciò, tuttavia, non significa che sarà più lento – in molti casi, la riallocazione sarà più veloce poiché è meno probabile che si promuovano gli array di grandi dimensioni in generazioni di raccolta dei rifiuti più elevati, che a loro volta possono mantenere il processo GC molto più veloce.

Senza conoscere il tuo scenario esatto e misurare in un profiler, non c’è modo di sapere quale è meglio nel tuo scenario.

Ho eseguito questo test:

 private static void Main(string[] args) { int defaultN = 1000; Stopwatch sw = new Stopwatch(); while (true) { Console.WriteLine("Enter test elements number:"); int n; if (!int.TryParse(Console.ReadLine(), out n)) n = defaultN; else defaultN = n; Console.WriteLine($"Test with {n} elements"); List list = Enumerable.Repeat(new object(), n).ToList(); sw.Start(); Clear(list); sw.Stop(); Console.WriteLine("Clear: {0} ms", sw.ElapsedTicks / 10000D); GC.Collect(); GC.WaitForPendingFinalizers(); List list2 = Enumerable.Repeat(new object(), n).ToList(); sw.Restart(); Reinitialize(list2); sw.Stop(); Console.WriteLine("Reinitialize: {0} ms", sw.ElapsedTicks / 10000D); GC.Collect(); GC.WaitForPendingFinalizers(); List list3 = Enumerable.Repeat(new object(), n).ToList(); sw.Restart(); ReinitializeAndCollect(list3); sw.Stop(); Console.WriteLine("ReinitializeAndCollect: {0} ms", sw.ElapsedTicks / 10000D); Console.WriteLine("==="); } } private static List Clear(List list) { list.Clear(); return list; } private static List Reinitialize(List list) => new List(); private static List ReinitializeAndCollect(List list) { list = new List(); GC.Collect(); GC.WaitForPendingFinalizers(); return list; } 

La mia conclusione basata su un risultato del mio ordinario processore i3 core:

In caso di migliaia di elementi, è meglio cancellare la lista. È veloce ed efficiente nella memoria.

Se la raccolta ha più di 100.000 elementi, la reinizializzazione diventa più attraente. Se dopo la profilatura pensi che ci sia un collo di bottiglia qui, usalo. La reinizializzazione sarà molto veloce, ma come mostra il test del terzo metodo, la futura raccolta dei rifiuti sarà tanto lenta quanto basta per cancellare l’elenco.

La risposta così breve è: se non hai profilato la tua domanda, usa Clear . Riusare gli oggetti è buono. Se lo hai fatto, sai già cosa fare.

Ciò dipenderà da molti fattori e, a lungo termine, probabilmente non contatterà (abbastanza da contare) nel tuo programma.

Da .Clear() docs. .Clear() è un’operazione O (n).

L’inizializzazione di una nuova istanza avrà il proprio overhead e (se si mantiene la raccolta della stessa lunghezza, un’operazione O (n): cioè n chiamate Add() ).

In realtà, l’unico modo per testare questo è impostare alcuni cronometri nel tuo programma e vedere qual è l’effetto se pensi davvero che ne valga la pena. Ogni probabilità; non ne vale la pena.

I miei pensieri sarebbero che se hai già creato una raccolta, Clear() , ecco perché c’è un metodo Clear() in primo luogo.

Mentre questo può essere frustrante, la risposta è che non dovrebbe avere importanza. La differenza di tempo tra i due sarà così piccola che probabilmente non farà alcuna differenza per la tua applicazione. Fai ciò che porta a un codice più pulito e comprensibile e prova a non programmare per micro-ottimizzazioni.

Clear() rimuoverà tutti gli elementi e manterrà la capacità esistente, mentre la creazione di una nuova lista richiederà almeno un’allocazione dall’heap gestito (probabilmente più come gli elementi vengono aggiunti se la capacità iniziale è piccola).

  • Se si dispone di un numero elevato di elementi e il numero di elementi è approssimativamente lo stesso in ogni iterazione, l’utilizzo di Clear è potenzialmente leggermente più veloce.

  • Se si dispone di un numero eccezionalmente elevato di elementi su una singola iterazione, un numero molto più piccolo nelle iterazioni successive, quindi l’utilizzo di Clear è potenzialmente più costoso, poiché si manterrà in memoria un elenco con una capacità inutilmente grande.

Naturalmente, in molti (la maggior parte?) Scenari la differenza sarà trascurabile.

Forse sto facendo qualcosa di fondamentalmente sbagliato qui, ma mentre si sviluppa un’applicazione ASP.NET in C # sto riscontrando una certa differenza quando si utilizza Clear () vs. new. Sto creando una pagina di statistiche con grafici, che hanno serie di dati. Per ogni grafico ho una sezione in cui faccio questo:

 chart = new ChartistChart() { Title = "My fancy chart" }; series = new List(); *some code for getting the statistics* chart.Series.Add(series); chartistLineCharts.Add(chart); 

poi segue un altro grafico.

 chart = new ChartistChart() { Title = "My second fancy chart" }; series = new List(); *some code for getting the statistics* chart.Series.Add(series); chartistLineCharts.Add(chart); 

Funziona perfettamente con le series vengono ridistribuite con new , ma quando lo faccio

 series.Clear(); 

invece, in realtà chart.Series la voce all’interno di chart.Series e chartistLineCharts modo che la pagina delle statistiche finisca per recuperare solo le serie dell’ultimo grafico. Presumo che ci sia un link, come un puntatore di memoria, qui e questo è un problema diverso da quello che è stato discusso in origine, ma questo è almeno un motivo per scegliere new su Clear() . Forse c’è un modo per evitarlo però.

Ho fatto diversi test per me stesso. I risultati (velocità) sono:

  • per le piccole liste, ad esempio 3 articoli, è più veloce creare nuove liste, ma la differenza non è grande
  • per 10 o più articoli in media è meglio cancellare le liste. Per i tipi di valore molto meglio (ad es. 3-4 volte), per tempi di valutazione del 20% migliori.

Ma alla fine, è meglio profilare l’applicazione e trovare i colli di bottiglia per l’intera applicazione.

Se i tuoi oggetti sono tipi di valore, utilizzerei Clear () per ridurre le allocazioni future della memoria. Altrimenti entrambi gli approcci sono quasi identici.