About the author

J Sawyer is a developer based in Houston, TX and loves to write code, especially ASP.NET and other web-related stuff.

He also loves to ride his Kawasaki Ninja.

But he doesn't code and ride at the same time.

Calendar

<<  March 2010  >>
MoTuWeThFrSaSu
22232425262728
1234567
891011121314
15161718192021
22232425262728
2930311234

View posts in large calendar

Linq Performance - Part I

October 8, 2008 7:00 PM

Well, it’s been a while since I did my initial review of some simple Linq performance tests. Since then, I’ve done a bit more testing of Linq performance and I’d like to share that. The results are enlightening, to say the least. I did this because I’ve gotten a lot of questions regarding the performance of Linq and, in particular, Linq to Sql – something that is common whenever there is a new data-oriented API. Now, let me also say that performance isn’t the only consideration … there are also considerations of functionality and ease of use, as well as the overall functionality of the API and its applicability to a wide variety of scenarios. I used the same methodology that I detailed in this previous post.

Now, all of the tests were against the AdventureWorks sample database’s Person.Contact table with some 20,000 rows. Not the largest table in the world, but it’s also a good deal larger that the much-beloved Northwind database. I also decided to re-run all of the tests a second time on my home PC (rather than my laptop) as the client and one of my test servers as the database server. The specs are as follows:

Client DB Server
AMD Athlon 64 X2 4400+ AMD Athlon 64 X2 4200+
4 GB RAM 2 GB RAM
Vista SP1 x64 Windows Server 2008 Standard x64
Visual Studio 2008 SP1 Sql Server 2008 x64

So, with that out of the way, let’s discuss the first test.

Simple Query

This is a simple “SELECT * FROM Person.Contact” query … nothing special or funky. From there, as with all of the tests, I loop through the results and assign them to temporary, local variables. An overview of the tests is below:

DataReaderIndex Uses a data reader and access the values using the strongly-typed GetXXX methods (i.e. GetString(int ordinal)). With this set, the ordinal is looked up using GetOrdinal before entering the loop to go over the resultset. This is my preferred method of using a DataReader.
int firstName = rdr.GetOrdinal("FirstName");
int lastName = rdr.GetOrdinal("LastName"); 
while (rdr.Read())
{
    string fullName = rdr.GetString(firstName) + rdr.GetString(lastName);
}
rdr.Close();
DataReaderHardCodedIndex This is the same as TestDataReaderIndex with the exception that the ordinal is not looked up before entering the loop to go over the resultset but is hard-coded into the application.
while (rdr.Read())
{
    string fullName = rdr.GetString(0) + rdr.GetString(1);
}
rdr.Close();
DataReaderNoIndex Again, using a reader, but not using the strongly-typed GetXXX methods. Instead, this is using the indexer property, getting the data using the column name as an object. This is how I see a lot of folks using Data Readers.
while (rdr.Read())
{
    string fullName = (string)rdr["FirstName"] + (string)rdr["LastName"];
}
rdr.Close();
LinqAnonType Uses Linq with an anonymous type
var contactNames = from c in dc.Contacts
                   select new { c.FirstName, c.LastName };
foreach (var contactName in contactNames)
{
    string fullName = contactName.FirstName + contactName.LastName;
}
LinqClass_Field Again, uses Linq but this time it’s using a custom type. In this class the values are stored in public fields, rather than variables.

IQueryable<AdvWorksName> contactNames = from c in dc.Contacts
                   select new AdvWorksName()
                    {FirstName= c.FirstName, LastName= c.LastName };
foreach (var contactName in contactNames)
{
    string fullName = contactName.FirstName + contactName.LastName;
}
DataSet This final test uses an untyped dataset. We won’t be doing a variation with a strongly-typed dataset for the select because they are significantly slower than untyped datasets. Also, the remoting format for the dataset is set to binary, which will help improve the performance for the dataset, especially as we get more records.
DataSet ds = new DataSet();
                ds.RemotingFormat = SerializationFormat.Binary; 
                SqlDataAdapter adp = new SqlDataAdapter(cmd);
                adp.Fill(ds);
                foreach (DataRow dr in ds.Tables[0].Rows)
                {
                    string fullName = dr.Field<String>("FirstName") + dr.Field<String>("LastName"); 
                }
                cnct.Close();
LinqClass_Prop This uses a custom Linq class with properties for the values.
IQueryable<AdvWorksNameProps> contactNames = from c in dc.Persons
                                        select new AdvWorksNameProps() { FirstName = c.FirstName, LastName = c.LastName };
foreach (var contactName in contactNames)
{
    string fullName = contactName.FirstName + contactName.LastName;
}
LinqClass_Ctor This uses the same Linq class as above but initializes the class by calling the constructor rather than binding to the properties.
IQueryable<AdvWorksNameProps> contactNames = from c in dc.Persons
                                        select new AdvWorksNameProps(c.FirstName,  c.LastName);
foreach (var contactName in contactNames)
{
    string fullName = contactName.FirstName + contactName.LastName;
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

If you are wondering why the different “flavors” of Linq … it’s because, when I first started re-running these tests for the blog, I got some strange differences that I hadn’t seen before between (what is now) LinqAnonType and LinqClassField. On examination, I found that these things made a difference and wanted to get a more rounded picture of what we were looking at here … so I added a couple of tests.

And the results …

 

 

  Average
LinqClass_Field 277.61
DataReaderIndex 283.43
DataReaderHardCodedIndex 291.17
LinqClass_Prop 310.76
DataSet 323.71
LinqAnonType 329.26
LinqClass_Ctor 370.20
DataReaderNoIndex 401.63

image

These results are actually quite different from what I saw when I ran the tests on a single machine … which is quite interesting and somewhat surprising to me. Linq still does very well when compared to DataReaders … depending on exactly how you implement the class. I didn’t expect that the version using the constructor would turn out to be the one that had the worst performance … and I’m not really sure what to make of that. I was surprised to see the DataSet do so well … it didn’t on previous tests, but in those cases, I also didn’t change the remoting format to binary; this does have a huge impact on the load performance, especially as the datasets get larger (XML gets pretty expensive when it starts getting big).

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

I’ve got more tests, but due to the sheer length of this post, I’m going to post them separately.



Tags: , , ,

.NET Stuff | Linq | Performance

Comments are closed