FC07 Replacing Linq.Emumerable.Any

Your own extension methods could be much faster

So now that we’ve established that even the simplest Linq.Enumerable.Any() extension method is slow, costing 143 instructions and at least one heap allocation for the enumerator. What should you do, especially when your source code is using it overall the place?

A simple solution is to add your own extension methods, put them in a static class in the base namespace, to hijack calls to Linq extension methods. Examples:

We’re adding Any extension methods for ICollection and IReadOnlyCollection interfaces, and a bunch of commonly used classes. Now for any usages with those classes and interfaces, the call will be redirected to your extension methods, which are much faster because they do not need enumeration.

For interfaces/classes with Count/Length properties, we can compare with 0. For some classes, Count property implementation could be quite expensive, we can use IsEmpty property when it’s available.

Notice we’re making a second improvement, the exception throwing after null reference check is changed to a method which allocates and throws exception:

This makes the extension methods smaller, so they’re more likely to be inlined. This is the recommended way of exception handling in such smaller methods used in lots of places inside mscorlib.dll. Inlining is an important technique to improve the performance of hot small methods.

You could even replace the null check and exception throwing with just return false. Personally, I prefer to avoid overuse of exceptions.

Let’s check the assembly instructions for the List version:

There is a null reference check first, then direct accessing internal _size field (at offset 0×18) of List which keeps the count. It just needs 8 instructions in total, a huge drop from 143 instructions in Linq implementation. Such small methods can be easily inlined.

This is the effect of removing null check:

Just single instruction to access the internal count and compare with 0.