Despite "The Cost of Premature Optimization", it's useful to know how things work so you can do basic things to avoid bad performance.  In the end, the example below is trivial and doesn't have a significant effect on performance (at least as it applies in my application).  However, knowledge of IEnumerable and yield can come in handy when it really counts.

As I was writing a bit of code, I wondered if order matters when it comes to query expressions.  Specifically, I have an enumerable that I was applying a Where and OrderBy to.  Ultimately, I was selecting the first item from the result.

I came back to the code later and decided to write an experiment.  What would happen if I changed the order of the Where and OrderBy?

`   1: [Test]`
`   2: public void TestExperiment()`
`   3: {`
`   4:     IEnumerable<int> numbers = new[] { 3, 16, 2, 19, 14, 5, 20, 8, 11, 7, 13, 6, 12, 9, 4, 15, 10, 18, 1, 17 };`
`   5:     var composite = from number in numbers`
`   6:                     select new { Number = number, Date = DateTime.Today.AddMonths( number ) };`
`   7:     int whereCount;`
`   8:     int orderByCount;`
`   9:     Console.WriteLine( "IEnumerable<Anonymous> : Comparing Anonymous.Date" );`
`  10:     Console.WriteLine();`
`  11:     Console.WriteLine( "Compare <= " );`
`  12:     Console.WriteLine( "Where then OrderBy" );`
`  13:     whereCount = 0;`
`  14:     orderByCount = 0;`
`  15:     composite`
`  16:         .Where( test =>`
`  17:             {`
`  18:                 whereCount++;`
`  19:                 return test.Date <= DateTime.Today.AddMonths( 5 );`
`  20:             } )`
`  21:         .OrderBy( test =>`
`  22:             {`
`  23:                 orderByCount++;`
`  24:                 return test.Date;`
`  25:             } )`
`  26:         .First();`
`  27:     Console.WriteLine( "    Where:" + whereCount );`
`  28:     Console.WriteLine( "    OrderBy:" + orderByCount );`
`  29:     Console.WriteLine( "OrderBy then Where" );`
`  30:     whereCount = 0;`
`  31:     orderByCount = 0;`
`  32:     composite`
`  33:         .OrderBy( test =>`
`  34:             {`
`  35:                 orderByCount++;`
`  36:                 return test.Date;`
`  37:             } )`
`  38:         .Where( test =>`
`  39:             {`
`  40:                 whereCount++;`
`  41:                 return test.Date <= DateTime.Today.AddMonths( 5 );`
`  42:             } )`
`  43:         .First();`
`  44:     Console.WriteLine( "    Where:" + whereCount );`
`  45:     Console.WriteLine( "    OrderBy:" + orderByCount );`
`  46:     Console.WriteLine();`
`  47:     Console.WriteLine( "Compare >= " );`
`  48:     Console.WriteLine( "Where then OrderBy" );`
`  49:     whereCount = 0;`
`  50:     orderByCount = 0;`
`  51:     composite`
`  52:         .Where( test =>`
`  53:             {`
`  54:                 whereCount++;`
`  55:                 return test.Date >= DateTime.Today.AddMonths( 5 );`
`  56:             } )`
`  57:         .OrderBy( test =>`
`  58:             {`
`  59:                 orderByCount++;`
`  60:                 return test.Date;`
`  61:             } )`
`  62:         .First();`
`  63:     Console.WriteLine( "    Where:" + whereCount );`
`  64:     Console.WriteLine( "    OrderBy:" + orderByCount );`
`  65:     Console.WriteLine( "OrderBy then Where" );`
`  66:     whereCount = 0;`
`  67:     orderByCount = 0;`
`  68:     composite`
`  69:         .OrderBy( test =>`
`  70:             {`
`  71:                 orderByCount++;`
`  72:                 return test.Date;`
`  73:             } )`
`  74:         .Where( test =>`
`  75:             {`
`  76:                 whereCount++;`
`  77:                 return test.Date >= DateTime.Today.AddMonths( 5 );`
`  78:             } )`
`  79:         .First();`
`  80:     Console.WriteLine( "    Where:" + whereCount );`
`  81:     Console.WriteLine( "    OrderBy:" + orderByCount );`
`  82: }`

Here are the results:

`IEnumerable<Anonymous> : Comparing Anonymous.DateCompare <= Where then OrderBy    Where:20    OrderBy:5OrderBy then Where    Where:1    OrderBy:20Compare >= Where then OrderBy    Where:20    OrderBy:16OrderBy then Where    Where:5    OrderBy:20`

What causes this phenomenon?  The use of IEnumerable and yield expressions.

A few things to consider when building query expressions:

• What are the relative efficiencies of the Where and OrderBy algorithms?
• Does the Where yield a relatively large result (as a percentage of the original data)?
• Is the question above difficult to answer because of the dynamic nature of your data?
• Would using negative logic change the result?
• <= versus >
• OrderBy versus OrderByDescending
• Are the Where and OrderBy predicates related?
• e.g.  You may filter by Status but order by Id
Can you think of other considerations?

posted on Saturday, July 12, 2008 8:41 AM
Filed Under [ .Net Productivity C# ]