I saw a LINQ query syntax in my project today which was counting items with a specific condition from a List
like this:
int temp = (from A in pTasks
where A.StatusID == (int)BusinessRule.TaskStatus.Pending
select A).ToList().Count();
I thought of refactoring it by rewriting it using Count(Func)
to make it more readable. I thought it would also be good performance-wise, so I wrote:
int UnassignedCount = pTasks.Count(x => x.StatusID == (int)BusinessRule.TaskStatus.Pending);
But when I check using StopWatch
, the time elapsed by the lambda expression is always more than the query syntax:
Stopwatch s = new Stopwatch();
s.Start();
int UnassignedCount = pTasks.Count(x => x.StatusID == (int)BusinessRule.TaskStatus.Pending);
s.Stop();
Stopwatch s2 = new Stopwatch();
s2.Start();
int temp = (from A in pTasks
where A.StatusID == (int)BusinessRule.TaskStatus.Pending
select A).ToList().Count();
s2.Stop();
Can somebody explain why it is so?
There is no performance difference between LINQ queries and Lambda expressions.
More importantly: when it comes to querying databases, LINQ is in most cases a significantly more productive querying language than SQL. Compared to SQL, LINQ is simpler, tidier, and higher-level. It's rather like comparing C# to C++.
Query syntax and method syntax are semantically identical, but many people find query syntax simpler and easier to read. Some queries must be expressed as method calls. For example, you must use a method call to express a query that retrieves the number of elements that match a specified condition.
I have simulated your situation. And yes, there is difference between execution times of these queries. But, the reason of this difference isn't syntax of the query. It doesn't matter if you have used method or query syntax. Both yields the same result because query expressions are translated into their lambda expressions before they’re compiled.
But, if you have paid attention the two queries aren't same at all.Your second query will be translated to it's lambda syntax before it's compiled (You can remove ToList()
from query, because it is redundant):
pTasks.Where(x => x.StatusID == (int)BusinessRule.TaskStatus.Pending).Count();
And now we have two Linq queries in lambda syntax. The one I have stated above and this:
pTasks.Count(x => x.StatusID == (int)BusinessRule.TaskStatus.Pending);
Now, the question is:
Why there is difference between execution times of these two queries?
Let's find the answer:
We can understand the reason of this difference by reviewing these:
- .Where(this IEnumerable<TSource> source, Func<TSource, bool> predicate).Count(this IEnumerable<TSource> source)
and
- Count(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
;
Here is the implementation of Count(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
:
public static int Count<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
if (source == null) throw Error.ArgumentNull("source");
if (predicate == null) throw Error.ArgumentNull("predicate");
int count = 0;
foreach (TSource element in source) {
checked {
if (predicate(element)) count++;
}
}
return count;
}
And here is the Where(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
:
public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
if (source == null)
throw Error.ArgumentNull("source");
if (predicate == null)
throw Error.ArgumentNull("predicate");
if (source is Iterator<TSource>)
return ((Iterator<TSource>)source).Where(predicate);
if (source is TSource[])
return new WhereArrayIterator<TSource>((TSource[])source, predicate);
if (source is List<TSource>)
return new WhereListIterator<TSource>((List<TSource>)source, predicate);
return new WhereEnumerableIterator<TSource>(source, predicate);
}
Let's pay an attention to Where()
implementation. It will return WhereListIterator()
if your collection is List, but Count()
will just iterate over source.
And in my opinion they have made some speed up in the implementation of WhereListIterator
. And after this we are calling Count()
method which takes no predicate as input and only will iterate on filtered collection.
And regarding to that speed up in the implementation of WhereListIterator
:
I have found this question in SO: LINQ performance Count vs Where and Count. You can read @Matthew Watson answer there. He explains the performance difference between these two queries. And the result is:
The Where
iterator avoids indirect virtual table call, but calls iterator methods directly.
As you see in that answer call
instruction will be emitted instead of callvirt
. And, callvirt
is slower than call
:
From bookCLR via C#
:
When the callvirt IL instruction is used to call a virtual instance method, the CLR discovers the actual type of the object being used to make the call and then calls the method polymorphically. In order to determine the type, the variable being used to make the call must not be null. In other words, when compiling this call, the JIT compiler generates code that verifes that the variable’s value is not null. If it is null, the callvirt instruction causes the CLR to throw a NullReferenceException. This additional check means that the callvirt IL instruction executes slightly more slowly than the call instruction.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With