Linq, Dataset, Recordset performance resultat
Jeg har i længere tid undret mig over en dårlig performance med anvendelse af LINQ, og nu er jeg gået på jagt efter synderen i tiden i databasen SQL Server.
I denne første test jeg har lavet har jeg dog ikke fundet synderen, men stadig et interessant resultat, som bekræfter mig i, at du ikke skal bruge et Dataset som bliver indlæst med data fra en SqlDataAdapter med Fill metoden, og her kan du se hvorfor:
Min test går i sin simpelhed ud på at læse 1.000, 10.000 og 100.000 rækker fra en simpel tabel og så måle, hvor lang tid det tager få de tre måder at hente data ud på fra en SQL Server.
Konklusion
Recordset dobbelt så hurtigt til små database kørsler
Ved små data mængder er LINQ ikke så hurtig som Recordset, men da tiden er så lav mærkes det ikke, hvis de er enkelte jobs, så det er reelt kun et problem, hvis du skal køre rigtig mange af disse små-jobs mod databasen så husk du er færdig på den halve tid med et Recordset.
Dataset skal anvendes, hvor mange af de samme data skal anvendes alsidsigt
Når der skal tælles, hvor mange rækker man har fået ud i et resultat, så må du bede til, at du ikke har anvendt Dataset metoden, det der trækker ned er helt klart at måden hvorpå man skal hive data ud for at få et Dataset ikke egner sig til små resultater, fordi man skal hente hele tabellen med en hel vildt masse egenskaber ud, og det tager bare tid.
Med 10.000 rækker er både LINQ og Recordset over dobbelt så hurtige som Dataset.
Når vi øger antallet af rækker med en faktor 10 til 10.000 rækker stiger test tiderne selvfølgelig, fordi databasen tager længere tid om at sende 10 gange så meget data, men overraskende meget synes jeg. LINQ metoden stiger med en faktor 4,5, Dataset metoden stiger med en faktor 10 og Recordset metoden stiger med en faktor 6,5.
Med 100.000 rækker begynder LINQ at hale gevaldigt ind på Recordset.
Når vi øver antallet af rækker yderligere med en faktor 10 til 100.000 rækker, ville jeg forvente samme stigningsgrad som før med fra 1.000 til 10.000, men nu stiger den endnu mere. LINQ metoden stiger med en faktor 8,7, Dataset metoden med en faktor 9 og Recordset metoden med en faktor 9,5.
Download Visual Studio 2008 Projektet
Testsystem
Hardware: Bærbar HP Elitebook, 3GB RAM, 2,53GHz Intel Core Duo, 32bit
Software: Windows 7 32bit, SQL Server Express 2009, Visual Studio 2008 Professional Developer Web Server
Testforløb
Jeg har opdelt min test i 4 forløb og hver eneste forløb er kørt 10 gange på samme metode for så til sidst at få en gennemsnits tidsforbrug som toneangivende konklusion.
LINQ måling metode
private void DoLinqTest() { DateTime dtLinqStart = DateTime.Now; using ( DB.dbDataContext db = new DB.dbDataContext() ) { // Execution code } DateTime dtLinqEnd = DateTime.Now; lblLinqTime.Text = ( dtLinqEnd - dtLinqStart ).ToString(); }
Dataset måling metode
private void DoDataSetTest() { DateTime dtDsStart = DateTime.Now; using ( SqlConnection dbCon = new SqlConnection( ConfigurationManager.ConnectionStrings[ "dbConnectionString" ].ConnectionString ) ) { using ( SqlDataAdapter sqlAdap = new SqlDataAdapter( "SELECT * FROM item", dbCon ) ) { DataSet ds = new DataSet(); sqlAdap.Fill( ds ); // Executing code } } DateTime dtDsEnd = DateTime.Now; lblDsTime.Text = ( dtDsEnd - dtDsStart ).ToString(); }
Recordset måling metode
private void DoRecordSetTest() { DateTime dtRsStart = DateTime.Now; using ( SqlConnection dbCon = new SqlConnection( ConfigurationManager.ConnectionStrings[ "dbConnectionString" ].ConnectionString ) ) { dbCon.Open(); using ( SqlCommand sqlCmd = new SqlCommand( "SELECT * FROM item", dbCon ) ) { using ( SqlDataReader rs = sqlCmd.ExecuteReader() ) { // Executing code } } } DateTime dtRsEnd = DateTime.Now; lblRsTime.Text = ( dtRsEnd - dtRsStart ).ToString(); }
1. forløb
En test som ses i Testresultat Sheet: 1000 List på at få 1000 database rækker ud som objekter af flg. type:
public class item { public int Id { get; set; } public string Name { get; set; } public string Description { get; set; } public DateTime Date { get; set; } public item() { } }
Executing LINQ code:
List<item> objItems = ( from itm in db.items select new item { Id = itm.id, Name = itm.name, Description = itm.description, Date = itm.date.Value } ).ToList();
Executing Dataset code:
List<item> items = new List<item>(); DataRow[] drs = ds.Tables[ 0 ].Select(); for ( int i = 0; i < drs.Length; i++ ) { object[] dbFields = drs[ i ].ItemArray; item itm = new item(); itm.Id = ( int )dbFields[ 0 ]; itm.Name = dbFields[ 1 ].ToString(); itm.Description = dbFields[ 2 ].ToString(); itm.Date = ( DateTime )dbFields[ 3 ]; items.Add( itm ); }
Executing Recordset code:
List<item> items = new List<item>(); while ( rs.Read() ) { item itm = new item(); itm.Id = rs.GetInt32( 0 ); itm.Name = rs.GetString( 1 ); itm.Description = rs.GetString( 2 ); itm.Date = rs.GetDateTime( 3 ); items.Add( itm ); }
2. forløb
En test som ses i Testresultat sheet: 1000 Count på at få antallet af rækker ud på en database tabel med 1.000 rækker
Executing LINQ code:
int count = ( from itm in db.items select new item { Id = itm.id, Name = itm.name, Description = itm.description, Date = itm.date.Value } ).Count();
Executing Dataset code:
int count = ds.Tables[ 0 ].Rows.Count;
Executing Recordset code:
int count = 0; while ( rs.Read() ) { count++; }
3. forløb
En test som ses i Testresultat sheet: 10000 List med samme Executing code som 1. forløb, men denne gang på at få 10.000 database rækker ud.
4. forløb
En test som ses i Testresultat sheet: 100000 List med samme Executing code som 1. forløb, men denne gang på at få 100.000 database rækker ud.
Testresultat
Download Visual Studio 2008 Projektet

[...] og det er ikke første gang jeg opdager det. Jeg opdagede det første gang på min blogpost Linq, Dataset, Recordset performance resultat, der tog det ca. 15 [...]