SonarSource / sonar-dotnet

Code analyzer for C# and VB.NET projects
https://redirect.sonarsource.com/plugins/csharp.html
GNU Lesser General Public License v3.0
790 stars 227 forks source link

Fix S6605 FP: Any is faster than Exists in .NET 9 #9665

Open marco-carvalho opened 1 month ago

marco-carvalho commented 1 month ago

Hello,

The rule S6605 may no longer be applicable for projects targeting .NET 9.0.

Recent benchmarks indicate that starting with .NET 9.0, Any is actually faster than Exists. Below are the benchmark results.

Code:

using System.Collections.Generic;
using System.Linq;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using BenchmarkDotNet.Running;

BenchmarkRunner.Run<AnyVsExistsBenchmark>();

[SimpleJob(RuntimeMoniker.Net80)]
[SimpleJob(RuntimeMoniker.Net90)]
[MemoryDiagnoser]
[RankColumn]
public class AnyVsExistsBenchmark
{
    private readonly List<int> _list = new() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

    [Benchmark]
    public bool Any()
    {
        return _list.Any(x => x == 9);
    }

    [Benchmark]
    public bool Exists()
    {
        return _list.Exists(x => x == 9);
    }
}

Results:

| Method | Job      | Runtime  | Mean      | Error     | StdDev    | Median    | Rank | Gen0   | Allocated |
|------- |--------- |--------- |----------:|----------:|----------:|----------:|-----:|-------:|----------:|
| Any    | .NET 8.0 | .NET 8.0 | 26.148 ns | 0.6048 ns | 1.7255 ns | 26.027 ns |    4 | 0.0095 |      40 B |
| Exists | .NET 8.0 | .NET 8.0 |  9.022 ns | 0.2092 ns | 0.4680 ns |  8.999 ns |    3 |      - |         - |
| Any    | .NET 9.0 | .NET 9.0 |  6.248 ns | 0.2396 ns | 0.6876 ns |  6.031 ns |    1 |      - |         - |
| Exists | .NET 9.0 | .NET 9.0 |  8.396 ns | 0.2399 ns | 0.6845 ns |  8.365 ns |    2 |      - |         - |

Maybe this rule for code targeting .NET 8.0 or below, and a new rule "Any" method should be used instead of the "Exists" for code targeting .NET 9.0 or above?

jilles-sg commented 1 month ago

Like https://github.com/SonarSource/sonar-dotnet/issues/9664#issuecomment-2357877600 , List.Exists remained unchanged while Any was optimized.

sebastien-marichal commented 1 month ago

Hello @marco-carvalho,

Thank you for pointing this out. As for #9664, we will take a look when we prepare for the .NET 9 release!

Have a great day!

Corniel commented 1 month ago

I think advising the other way around does not make sense. We're dealing with a difference of 2 nanoseconds. You could consider report to Microsoft thought, as it is strange that a native method (.Exists) turns out to be slower than an extension method.