Skip to content

Commit

Permalink
Remove from SoanrWay and update benchmarks (#4450)
Browse files Browse the repository at this point in the history
  • Loading branch information
martin-strecker-sonarsource authored Oct 29, 2024
1 parent 3e93c74 commit 8beccff
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 36 deletions.
2 changes: 1 addition & 1 deletion rules/S6603/metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"ruleSpecification": "RSPEC-6603",
"sqKey": "S6603",
"scope": "All",
"defaultQualityProfiles": ["Sonar way"],
"defaultQualityProfiles": [],
"quickfix": "targeted"
}

128 changes: 93 additions & 35 deletions rules/S6603/resources-dotnet.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,52 @@

=== Documentation

* https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.list-1.trueforall[List<T>.TrueForAll(Predicate<T>)]
* https://learn.microsoft.com/en-us/dotnet/api/system.array.trueforall[Array.TrueForAll<T>(T[\], Predicate<T>)]
* https://learn.microsoft.com/en-us/dotnet/api/system.collections.immutable.immutablelist-1.trueforall[ImmutableList<T>.TrueForAll(Predicate<T>)]
* https://learn.microsoft.com/en-us/dotnet/api/system.collections.immutable.immutablelist-1.builder.trueforall[ImmutableList<T>.Builder.TrueForAll(Predicate<T>)]
* https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.all[Enumerable.All<TSource>]
* Microsoft Learn - https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.list-1.trueforall[List<T>.TrueForAll(Predicate<T>)]
* Microsoft Learn - https://learn.microsoft.com/en-us/dotnet/api/system.array.trueforall[Array.TrueForAll<T>(T[\], Predicate<T>)]
* Microsoft Learn - https://learn.microsoft.com/en-us/dotnet/api/system.collections.immutable.immutablelist-1.trueforall[ImmutableList<T>.TrueForAll(Predicate<T>)]
* Microsoft Learn - https://learn.microsoft.com/en-us/dotnet/api/system.collections.immutable.immutablelist-1.builder.trueforall[ImmutableList<T>.Builder.TrueForAll(Predicate<T>)]
* Microsoft Learn - https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.all[Enumerable.All<TSource>]

=== Benchmarks

[options="header"]
|===
| Method | Runtime | Mean | Standard Deviation | Allocated
| TrueForAll | .NET 7.0 | 1.302 ms | 0.0027 ms | 1 B
| All | .NET 7.0 | 6.279 ms | 0.0181 ms | 40004 B
| TrueForAll | .NET Framework 4.6.2 | 1.105 ms | 0.0142 ms | -
| All | .NET Framework 4.6.2 | 4.968 ms | 0.0143 ms | 40128 B
| Method | Runtime | Categories | Mean | Standard Deviation | Allocated
| ArrayAll | .NET 8.0 | Array | 109.25 μs | 1.767 μs | 32 B
| ArrayTrueForAll | .NET 8.0 | Array | 45.01 μs | 0.547 μs | -
| | | | | |
| ArrayAll | .NET 9.0 | Array | 22.28 μs | 0.254 μs | -
| ArrayTrueForAll | .NET 9.0 | Array | 37.60 μs | 0.382 μs | -
| | | | | |
| ArrayAll | .NET Framework 4.8.1 | Array | 495.90 μs | 4.342 μs | 40 B
| ArrayTrueForAll | .NET Framework 4.8.1 | Array | 164.52 μs | 2.030 μs | -
| | | | | |
| ImmutableListAll | .NET 8.0 | ImmutableList<T> | 940.29 μs | 5.600 μs | 72 B
| ImmutableListTrueForAll | .NET 8.0 | ImmutableList<T> | 679.46 μs | 2.371 μs | -
| | | | | |
| ImmutableListAll | .NET 9.0 | ImmutableList<T> | 922.43 μs | 14.564 μs | 72 B
| ImmutableListTrueForAll | .NET 9.0 | ImmutableList<T> | 692.31 μs | 8.897 μs | -
| | | | | |
| ImmutableListAll | .NET Framework 4.8.1 | ImmutableList<T> | 4,578.72 μs | 77.920 μs | 128 B
| ImmutableListTrueForAll | .NET Framework 4.8.1 | ImmutableList<T> | 4,393.49 μs | 122.061 μs | -
| | | | | |
| ImmutableListBuilderAll | .NET 8.0 | ImmutableList<T>.Builder | 970.45 μs | 13.598 μs | 73 B
| ImmutableListBuilderTrueForAll | .NET 8.0 | ImmutableList<T>.Builder | 687.82 μs | 6.142 μs | -
| | | | | |
| ImmutableListBuilderAll | .NET 9.0 | ImmutableList<T>.Builder | 981.17 μs | 12.966 μs | 72 B
| ImmutableListBuilderTrueForAll | .NET 9.0 | ImmutableList<T>.Builder | 710.19 μs | 16.195 μs | -
| | | | | |
| ImmutableListBuilderAll | .NET Framework 4.8.1 | ImmutableList<T>.Builder | 4,780.50 μs | 43.282 μs | 128 B
| ImmutableListBuilderTrueForAll | .NET Framework 4.8.1 | ImmutableList<T>.Builder | 4,493.82 μs | 76.530 μs | -
| | | | | |
| ListAll | .NET 8.0 | List<T> | 151.12 μs | 2.028 μs | 40 B
| ListTrueForAll | .NET 8.0 | List<T> | 58.03 μs | 0.493 μs | -
| | | | | |
| ListAll | .NET 9.0 | List<T> | 22.14 μs | 0.327 μs | -
| ListTrueForAll | .NET 9.0 | List<T> | 46.01 μs | 0.327 μs | -
| | | | | |
| ListAll | .NET Framework 4.8.1 | List<T> | 619.86 μs | 6.037 μs | 48 B
| ListTrueForAll | .NET Framework 4.8.1 | List<T> | 208.49 μs | 2.340 μs | -
|===

==== Glossary
Expand All @@ -29,42 +60,69 @@ The results were generated by running the following snippet with https://github.

[source,csharp]
----
private List<int> data;
// Explicitly cache the delegates to avoid allocations inside the benchmark.
private readonly static Func<int, bool> ConditionFunc = static x => x == Math.Abs(x);
private readonly static Predicate<int> ConditionPredicate = static x => x == Math.Abs(x);
[Params(10_000)]
private List<int> list;
private ImmutableList<int> immutableList;
private ImmutableList<int>.Builder immutableListBuilder;
private int[] array;
[Params(100_000)]
public int N { get; set; }
[GlobalSetup]
public void Setup() =>
data = Enumerable.Range(0, N).Select(x => 42).ToList();
[Benchmark]
public void TrueForAll()
public void GlobalSetup()
{
for (var i = 0; i < N; i++)
{
_ = data.TrueForAll(x => x == 42); // List<T>.TrueForAll
}
list = Enumerable.Range(0, N).Select(x => N - x).ToList();
immutableList = ImmutableList.CreateRange(list);
immutableListBuilder = ImmutableList.CreateBuilder<int>();
immutableListBuilder.AddRange(list);
array = list.ToArray();
}
[Benchmark(Baseline = true)]
public void All()
{
for (var i = 0; i < N; i++)
{
_ = data.All(x => x == 42); // Enumerable.All<TSource>
}
}
[BenchmarkCategory("List<T>"), Benchmark]
public bool ListAll() =>
list.All(ConditionFunc);
[BenchmarkCategory("List<T>"), Benchmark(Baseline = true)]
public bool ListTrueForAll() =>
list.TrueForAll(ConditionPredicate);
[BenchmarkCategory("ImmutableList<T>"), Benchmark(Baseline = true)]
public bool ImmutableListAll() =>
immutableList.All(ConditionFunc);
[BenchmarkCategory("ImmutableList<T>"), Benchmark]
public bool ImmutableListTrueForAll() =>
immutableList.TrueForAll(ConditionPredicate);
[BenchmarkCategory("ImmutableList<T>.Builder"), Benchmark(Baseline = true)]
public bool ImmutableListBuilderAll() =>
immutableListBuilder.All(ConditionFunc);
[BenchmarkCategory("ImmutableList<T>.Builder"), Benchmark]
public bool ImmutableListBuilderTrueForAll() =>
immutableListBuilder.TrueForAll(ConditionPredicate);
[BenchmarkCategory("Array"), Benchmark(Baseline = true)]
public bool ArrayAll() =>
array.All(ConditionFunc);
[BenchmarkCategory("Array"), Benchmark]
public bool ArrayTrueForAll() =>
Array.TrueForAll(array, ConditionPredicate);
----

Hardware configuration:

[source]
----
BenchmarkDotNet=v0.13.5, OS=Windows 10 (10.0.19045.2846/22H2/2022Update)
12th Gen Intel Core i7-12800H, 1 CPU, 20 logical and 14 physical cores
.NET SDK=7.0.203
[Host] : .NET 7.0.5 (7.0.523.17405), X64 RyuJIT AVX2
.NET 7.0 : .NET 7.0.5 (7.0.523.17405), X64 RyuJIT AVX2
.NET Framework 4.6.2 : .NET Framework 4.8 (4.8.4614.0), X64 RyuJIT VectorSize=256
BenchmarkDotNet v0.14.0, Windows 11 (10.0.22631.4317/23H2/2023Update/SunValley3)
11th Gen Intel Core i7-11850H 2.50GHz, 1 CPU, 16 logical and 8 physical cores
[Host] : .NET Framework 4.8.1 (4.8.9277.0), X64 RyuJIT VectorSize=256
.NET 8.0 : .NET 8.0.10 (8.0.1024.46610), X64 RyuJIT AVX-512F+CD+BW+DQ+VL+VBMI
.NET 9.0 : .NET 9.0.0 (9.0.24.47305), X64 RyuJIT AVX-512F+CD+BW+DQ+VL+VBMI
.NET Framework 4.8.1 : .NET Framework 4.8.1 (4.8.9277.0), X64 RyuJIT VectorSize=256
----
2 changes: 2 additions & 0 deletions rules/S6603/why-dotnet.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

Both the `List.TrueForAll` method and the `IEnumerable.All` method can be used to check if all list elements satisfy a given condition in a collection. However, `List.TrueForAll` can be faster than `IEnumerable.All` for `List` objects. The performance difference may be minor for small collections, but for large collections, it can be noticeable.

It is important to enable this rule with caution, as performance outcomes can vary significantly across different runtimes. Notably, the https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-9/#collections[performance improvements in .NET 9] have brought `All` closer to the performance of collection-specific `TrueForAll` methods in most scenarios.

*Applies to*

* https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.list-1.trueforall[List]
Expand Down

0 comments on commit 8beccff

Please sign in to comment.