I'm here to talk about C# performance optimization (again). And about why you should stop using the .Any()
LINQ method with Lists and Arrays and embrace the old-school .Count
and .Length
instead. Don't want to point any fingers, but I see people do it all the time, even in (cough) Microsoft's own .NET source codes.
Armed with BenchmarkDotNet, a tool more trustworthy than my coffee machine, I embarked on a quest to compare .Any()
with .Length > 0
for a 100-element integer array. The results were so shocking, I almost spilled my coffee.
Method | Mean | Error | StdDev | Allocated |
---|---|---|---|---|
Any | 5.5362 ns | 1.0495 ns | 0.0575 ns | - |
Length | 0.2413 ns | 0.1316 ns | 0.0072 ns | - |
Yes, you're seeing it right. Using .Length
is approximately 30 times faster than .Any()
. I could practically hear my CPU laughing at the inefficiency of .Any()
.
But wait, there's more! I didn't stop there. I decided to test this with lists as well (you see where this is going).
Method | Mean | Error | StdDev | Allocated |
---|---|---|---|---|
Any | 2.3934 ns | 0.0325 ns | 0.0018 ns | - |
Count | 0.0000 ns | 0.0000 ns | 0.0000 ns | - |
The List is more efficient at Any()
but the result for .Count
was so fast, it broke the laws of physics and registered as 0.0000 nanoseconds (well, it actually didn't, it was simply "indistinguishable from the empty method duration" as the benchmark told me). I mean, how can you compete with that?
You probably thought Any()
is smart enough to detect lists, arrays and collection that are passed to it. Well, it's not. It just calls .MoveNext()
once and looks what happens next.
P.S. Fun fact - actually, if you enable "Code Analyser" in Visual Studio it will literally tell you to "Prefer comparing 'Length' to 0 rather than using 'Any()', both for clarity and for performance".