Wednesday, June 17, 2009

Linq Performance

I needed to clear all the textboxes in a windows form without specifying each and every textbox. Easy enough to do in C#:



foreach (Control c in Controls)
{
if (c is TextBox)
((TextBox)c).Clear();

if (c is ListBox)
((ListBox)c).Items.Clear();

if (c is Panel)
{
foreach (Control c2 in c.Controls)
{
if (c2 is TextBox)
{
((TextBox)c2).Clear();
}
}
}
}

To me, that just seemed expensive. I haven't had a chance to play with Linq, until now. So I came up with the Linq equivilant:



foreach (TextBox tb in from Control control in Controls
where control is TextBox
select (TextBox)control)
{
tb.Clear();
}

foreach (ListBox lb in from Control control in Controls
where control is ListBox
select (ListBox)control)
{
lb.Items.Clear();
}

foreach (TextBox tb in from Control control in panel1.Controls
where control is TextBox
select (TextBox)control)
{
tb.Clear();
}

For me, this is actually easier to read! OK, but performance must be horrible, but I'm not one to judge without an actual test. I used the System.Diagnostics.Stopwatch class like this:



System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch();
stopwatch.Start();
// Do stuffstopwatch.Stop();
MessageBox.Show(string.Format("linq: {0:#,0}", stopwatch.ElapsedTicks));


I had some very interesting numbers! For a form with about 25 controls, I got these numbers:
Linq averaged 3,700,000 ticks on first run
Foreach averaged 4,400,000 ticks on first run


Clicking the Clear button a second time resulted in faster times for both Linq and Foreach, probably due to a form of caching:
Linq averaged 333,000 ticks on subsequent runs
Foreach averaged 1,010,000 ticks on subsequent runs

So, Linq must be optimized for ControlCollection, which is derived from ArrangedElementCollection, which implements IList, ICollection, IEnumerable, ICloneable. I'll have to do more investigation to see where foreach is faster than Linq and post that result later.

No comments:

Post a Comment