Originariamente volevo chiedere il modo più veloce per interrogare un Datatable per una riga speciale.Sorprendenti differenze nelle prestazioni: List.Constains, SortedList.ContainsKey, DataRowCollection.Contains, DataTable.Select, DataTable.FindBy
Ho testato 5 metodi diversi per le loro prestazioni con un risultato sorprendente (per me).
Sfondo: Ho creato una vista in un database MS Sql-Server 2005. Questa vista ha un conteggio totale di 6318 righe. Perché devo controllare molto spesso se un dato ID esiste in questa vista, mi chiedevo quale sia il modo più efficace per farlo. Ho creato un DataAdapter in un dataset fortemente tipizzato che restituisce tutte le righe e riempie un Datatable. Il mio primo approccio è stato quello di creare un elenco generico condiviso (di Int32) e riempirlo con gli ID dalla vista una volta all'avvio dell'applicazione. Quindi utilizzare List.Contains per verificare se l'ID corrente è in questo elenco. Poiché tutte le righe sono distinte, mi sono chiesto se non è più veloce utilizzare SortedList e il suo metodo ContainsKey. Poi ho controllato anche le prestazioni di accesso diretto al Datable con il suo Select-Method, il suo generato automaticamente (quando la colonna è definita come chiave primaria) FindBy-method e, ultimo ma non meno importante, il metodo DatarowCollection.Contains. Quindi ho 5 metodi per controllare se il mio ID è in quella vista (o lista mappata/lista ordinata).
Ho misurato le loro prestazioni con lo System.Diagnostics.StopWatch e ottenuto alcuni risultati interessanti. Ho pensato che SortedList.ContainsKey deve essere più veloce di List.Contains perché sono distinti e ordinati ma è vero il contrario. Ma la cosa più sorprendente per me è che il DataRowCollection.Contains-Method (che prima avevo dimenticato) è di gran lunga il più veloce. È anche 50 volte più veloce del metodo dataTable.FindBy.
- Che cosa ha causato queste differenze?
- Ho dimenticato un modo migliore?
- Il mio metodo di misurazione è corretto (penso che dovrei eseguirne il ciclo e prendere quei valori)?
- I valori sono trasferibili o dipendenti dalla dimensione del Datatable/Collection?
- Dopo il mio aggiornamento (1000000 iterazioni) ContainsKey è il più veloce. È perché controllo sempre lo stesso ID o in generale? Esiste una sorta di SortedList senza il sovraccarico di KeyValue-Pair di un dizionario?
Risultati [per 1000000 iterazioni *]
- periodo 1 = SortedList.ContainsKey = Ø 0.65634 [238.1095] ms
- periodo 2 = List.Contains = Ø 0.06802 [57045,37,955 mila] ms
- Periodo 3 = DataTable.FindByIdData (metodo generato automaticamente) = Ø 0,31580 [1542.62345] ms
- periodo 4 = DataTable.Select = Ø 0,27,79 mila [26029.39635] ms
periodo 5 = DataRowCollection.Contains = Ø 0,00,638 mila [1202.79735] ms
1.) Timespan 1: 0,6913 ms Timespan 2: 0,1053 ms Timespan 3: 0,3279 ms Timespan 4: 0,1002 ms Timespan 5: 0,0056 ms 2.) Timespan 1: 0,6405 ms Timespan 2: 0,0588 ms Timespan 3: 0,3112 ms Timespan 4: 0,3872 ms Timespan 5: 0,0067 ms 3.) Timespan 1: 0,6502 ms Timespan 2: 0,0588 ms Timespan 3: 0,3092 ms Timespan 4: 0,1268 ms Timespan 5: 0,007 ms 4.) Timespan 1: 0,6504 ms Timespan 2: 0,0586 ms Timespan 3: 0,3092 ms Timespan 4: 0,3893 ms Timespan 5: 0,0063 ms 5.) Timespan 1: 0,6493 ms Timespan 2: 0,0586 ms Timespan 3: 0,3215 ms Timespan 4: 0,386 ms Timespan 5: 0,0063 ms Timespan 1: 0,6913 0,6405 0,6502 0,6504 0,6493 = Ø 0,65634 Timespan 2: 0,1053 0,0588 0,0588 0,0586 0,0586 = Ø 0,06802 Timespan 3: 0,3279 0,3112 0,3092 0,3092 0,3215 = Ø 0,31580 Timespan 4: 0,1002 0,3872 0,1268 0,3893 0,3860 = Ø 0,27790 Timespan 5: 0,0056 0,0067 0,0070 0,0063 0,0063 = Ø 0,00638
E per il bene di una parte completezza della fonte VB.Net:
Dim applies As Boolean
Dim clock As New System.Diagnostics.Stopwatch
clock.Start()
For i As Int32 = 1 To 1000000
applies = sortedListAC17NextClaims.ContainsKey(myClaim.idData)
Next
clock.Stop()
Dim timeSpan1 As String = "Timespan 1: " & clock.Elapsed.TotalMilliseconds.ToString & " ms"
clock.Reset()
clock.Start()
For i As Int32 = 1 To 1000000
applies = listAC17NextClaims.Contains(myClaim.idData)
Next
clock.Stop()
Dim timeSpan2 As String = "Timespan 2: " & clock.Elapsed.TotalMilliseconds.ToString & " ms"
clock.Reset()
clock.Start()
For i As Int32 = 1 To 1000000
applies = Not MyDS.AC17NextClaims.FindByIdData(myClaim.idData) Is Nothing
Next
clock.Stop()
Dim timeSpan3 As String = "Timespan 3: " & clock.Elapsed.TotalMilliseconds.ToString & " ms"
clock.Reset()
clock.Start()
For i As Int32 = 1 To 1000000
applies = MyDS.AC17NextClaims.Select("idData=" & myClaim.idData).Length > 0
Next
clock.Stop()
Dim timeSpan4 As String = "Timespan 4: " & clock.Elapsed.TotalMilliseconds.ToString & " ms"
clock.Reset()
clock.Start()
For i As Int32 = 1 To 1000000
applies = MyDS.AC17NextClaims.Rows.Contains(myClaim.idData)
Next
clock.Stop()
Dim timeSpan5 As String = "Timespan 5: " & clock.Elapsed.TotalMilliseconds.ToString & " ms"
UPDATE: ho cambiato i miei risultati e la fonte sopra. Nelle parentesi quadre sono presenti i valori per 1000000 iterazioni. Ora il risultato è completamente diverso. Il metodo più veloce ora è sicuramente ContainsKey di SortedList.
UPDATE 2: ho dimenticato l'alternativa di utilizzare List.BinarySearch. Questo sembra essere più veloce per me:
clock.Start() For i As Int32 = 1 To 1000000 applies = listAC17NextClaims.BinarySearch(myClaim.idData) > -1 Next clock.Stop()
ha solo bisogno di 219,1805 ms per eseguire iterazioni 1000000 e quindi è il più veloce senza il sovraccarico di un SortedList-KeyValue-Pair. Posso usarlo senza ordinare l'elenco perché il DataAdapter ha riempito il datatable con una clausola Order By.
Esegui l'ultimo con 600M elementi nella tabella e cerca due volte con ciascun metodo, scartando i tempi per la prima esecuzione di ciascuno. –
Ciò richiederebbe troppo tempo e sarebbe un altro requisito oltre al mio. –