2011-11-06 22 views
7

Ho sperimentato modi di leggere dati da un server SQL il più rapidamente possibile e ho trovato una scoperta interessante. Se leggo i dati in un List<object[]> anziché in un List<string[]>, le prestazioni aumentano di oltre il doppio.Elenco prestazioni SqlDataReader <string[]> o Elenco <object[]>

Sospetto che ciò sia dovuto al fatto che non è necessario chiamare il metodo ToString() nei campi, ma ho sempre pensato che l'utilizzo di oggetti avesse un impatto negativo sulle prestazioni.

C'è qualche motivo per non utilizzare un elenco di matrici di oggetti invece di matrici di stringhe?

EDIT: Un pensiero che ho avuto è stata la dimensione di archiviazione di questi dati. La memorizzazione dei dati negli array di oggetti richiede più spazio rispetto alle stringhe?

Ecco il mio codice di prova:

private void executeSqlObject() 
    { 
     List<object[]> list = new List<object[]>(); 

     using (SqlConnection cnn = new SqlConnection(_cnnString)) 
     { 
      cnn.Open(); 
      SqlCommand cmd = new SqlCommand("select * from test_table", cnn); 

      SqlDataReader reader = cmd.ExecuteReader(); 

      int fieldCount = reader.FieldCount; 

      while (reader.Read()) 
      { 
       object[] row = new object[fieldCount]; 

       for (int i = 0; i < fieldCount; i++) 
       { 
        row[i] = reader[i]; 
       } 
       list.Add(row); 
      } 
     } 
    } 

    private void executeSqlString() 
    { 
     List<string[]> list = new List<string[]>(); 

     using (SqlConnection cnn = new SqlConnection(_cnnString)) 
     { 
      cnn.Open(); 
      SqlCommand cmd = new SqlCommand("select * from test_table", cnn); 

      SqlDataReader reader = cmd.ExecuteReader(); 

      int fieldCount = reader.FieldCount; 

      while (reader.Read()) 
      { 
       string[] row = new string[fieldCount]; 

       for (int i = 0; i < fieldCount; i++) 
       { 
        row[i] = reader[i].ToString(); 
       } 
       list.Add(row); 
      } 
     } 
    } 

    private void runTests() 
    { 
     Stopwatch watch = new Stopwatch(); 
     for (int i = 0; i < 10; i++) 
     { 
      watch.Start(); 
      executeSqlObject(); 
      Debug.WriteLine("Object Time: " + watch.ElapsedMilliseconds.ToString()); 
      watch.Reset(); 
     } 
     for (int i = 0; i < 10; i++) 
     { 
      watch.Start(); 
      executeSqlString(); 
      Debug.WriteLine("String Time: " + watch.ElapsedMilliseconds.ToString()); 
      watch.Reset(); 
     } 
    } 

E i risultati:

Object Time: 879 
Object Time: 812 
Object Time: 825 
Object Time: 882 
Object Time: 880 
Object Time: 905 
Object Time: 815 
Object Time: 799 
Object Time: 823 
Object Time: 817 
Average: 844 

String Time: 1819 
String Time: 1790 
String Time: 1787 
String Time: 1856 
String Time: 1795 
String Time: 1731 
String Time: 1792 
String Time: 1799 
String Time: 1762 
String Time: 1869 
Average: 1800 
+1

Non posso discutere con i risultati. Dovresti anche avvolgere i tuoi lettori (e anche i comandi) nell'uso delle istruzioni perché possono perdere memoria. –

+0

Ci deve essere qualcosa di sbagliato nel test ... Rispetto alla lettura dei dati dal database, fare in modo che il tipo di stringa check * dovrebbe * essere trascurabile. – Guffa

+0

Per curiosità, è diverso se si lancia semplicemente il valore del lettore su una stringa ('row [i] = (string) reader [i];') invece di chiamare 'ToString()' su di esso, o invece, usando il built-in 'SqlDataReader.GetString()' metodo per recuperare il valore ('row [i] = reader.GetString (i);')? (Aassumendo tutti i valori di colonna sono stringhe.) –

risposta

8

object aggiunge solo in testa se si stanno causando ulteriore boxe. E anche allora, questo impatto è abbastanza minimo. Nel tuo caso, reader[i]sempre restituisce object. Lo hai già come object, indipendentemente dal fatto che si tratti di un riferimento a una stringa o di un int, ecc. Del corso chiamando il .ToString(); nella maggior parte dei casi (int, DateTime, ecc.) ciò riguarda sia la formattazione del codice e l'assegnazione di una (o più) stringa aggiuntiva. Passando a string si è cambiando i dati (in caso contrario, IMO - ad esempio, non è più possibile eseguire correttamente le ordinazioni sulle date, ad esempio) e aggiungendo un sovraccarico. Il caso limite qui è se tutte le colonne sono già effettivamente stringhe - nel qual caso basta aggiungere alcune chiamate al metodo virtuale (ma non un lavoro reale extra).

Per informazioni, se siete interessati a prestazioni non ottimali, consiglio vivamente di osservare le micro-ORM come dapper. Sono fortemente ottimizzati, ma evitano il peso degli ORM "pieni". Per esempio, in Dapper:

var myData = connection.Query<TypedObject>("select * from test_table").ToList(); 

sarà, mi aspetto, eseguire molto paragonabile mentre si dà i dati degli oggetti fortemente tipizzati.

+0

Marc, grazie per le buone informazioni. In riferimento alla soluzione micro-ORM, questa soluzione non mi richiederebbe di conoscere il set di dati che sto recuperando prima di eseguire la query? Ho bisogno che questa applicazione sia agnostica dei dati che sta recuperando – ChandlerPelhams

+0

@ChandlerPelhams che non era chiaro dalla domanda, p Supponendo che non si possa passare un 'T' down via generici (nella maggior parte delle applicazioni, * parte * del chiamante sa come sono i dati), quindi effettivamente qualcosa di più generico come 'oggetto []' o, come note "lowds", 'DataTable', potrebbe essere più appropriato. –

1

C'è qualche motivo per non utilizzare un elenco di matrici di oggetti invece di matrici di stringhe?

Sarebbe dipenderà da quello che si voleva fare con i valori recuperati dopo li hai nelle matrici, se sei felice per il trattamento di ogni valore come un oggetto poi avere un elenco di oggetti va bene, ma se vuoi trattarli come stringhe, poi a un certo punto dovrai convertire/eseguire il cast dell'oggetto su una stringa in modo da poter sostenere il costo da qualche parte.

Come Cory ha menzionato se si sta leggendo il valore come una stringa da SqlDataReader, è necessario testare utilizzando il metodo GetString (int) anziché chiamare ToString() sul valore e utilizzarlo come benchmark.

In alternativa, anziché utilizzare gli array, è possibile leggere i valori in un DataSet che potrebbe risultare più semplice da utilizzare successivamente.

Alla fine della giornata, la cosa migliore dipende molto da come si desidera utilizzare i risultati dopo averli recuperati dal database.

+0

lol, usabilità di DataSet vs Elenco di array ... penso che sia un argomento per un'altra domanda SO :) – lowds

+0

@ Marc - Anche a me :) – ChandlerPelhams

+0

Nel caso indicato, sembra (commenti) che il codice non possa conoscere il layout , nel qual caso devo notare che DataTable potrebbe effettivamente essere appropriato. –