Is this really true for the example? To me it seems that the implementation for .Any actually uses .Count when available, see https://github.com/dotnet/runtime/blob/main/src/libraries/Sy...
When using LINQ also be aware that .First(predicate) is significantly slower than .Where(predicate).First() when called on List<T> and T[]. This is true for essentially all methods like Last, Single, Count etc. Don't trust Visual Studio when it's telling you to "optimize" this.
But if you want the last bit of performance, you shouldn't use LINQ anyways.
The difference, as @sbelskie already mentioned, is that Where has an optimization for List, while First only uses the naive enumerator.
On a positive side, I can assure you I know how to run such benchmarks properly - i.e. things like warmup, explicit GC before / after are just some of the aspects taken into account. If you're curious about some other benchmarks I ran in past, check out https://itnext.io/geting-4x-speedup-with-net-core-3-0-simd-i... and https://github.com/alexyakunin/GCBurn
I'm interested, what's the advantage for you to using ILogger instead?
I turned it on mostly to show what you can expect from a service that runs for a while (more than a few minutes?) in a typical server-side scenario after migration to .NET 6 - IMO it's totally reasonable to turn PGO on for nearly any service of this kind.
- Use BenchmarkDotNet[0] for general measurements and Visual Studio profiler tools for detailed inspection. They help a lot.
- Memory allocations matter. Using capturing lambdas, LINQ, even foreach on interfaces introduce allocations and slows down the application. You can use ClrHeapAllocationAnalyzer[1] to find these hidden allocations.
- Using abstractions with interfaces and casting back to concrete types cause some overhead, though PGO will probably eliminate most of these.
- Use LINQ cautiously as its variants are mostly slower than explicit coding. E.g. .Any() vs .Count == 0
- Checking Logger.IsEnabled() before calling Logger.Debug() etc. helps a lot. You can even automate this with Fody [2], but it breaks Edit&Continue and possibly .NET Hot Reload too, so it may hinder your productivity.
[0] https://github.com/dotnet/BenchmarkDotNet
[1] https://github.com/microsoft/RoslynClrHeapAllocationAnalyzer
[2] https://github.com/jorisdebock/LoggerIsEnabled.Fody