Skip to content

Commit 781844b

Browse files
committed
Improve Count(predicate) from array performance
1 parent 853e0b1 commit 781844b

File tree

3 files changed

+73
-1
lines changed

3 files changed

+73
-1
lines changed

sandbox/Benchmark/Benchmarks/AllAnyPredicateWithCapturedLambda.cs

+31
Original file line numberDiff line numberDiff line change
@@ -79,3 +79,34 @@ public bool Linq()
7979
return _nums.All(i => i <= _target);
8080
}
8181
}
82+
83+
[GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)]
84+
public class CountPredicateWithCapturedLambda
85+
{
86+
[Params(10, 100, 1000, 10000)]
87+
public int N;
88+
89+
int[] _nums = default!;
90+
int _target;
91+
92+
[GlobalSetup]
93+
public void Setup()
94+
{
95+
_nums = Enumerable.Range(1, N).ToArray();
96+
_target = N;
97+
}
98+
99+
[Benchmark]
100+
[BenchmarkCategory(Categories.ZLinq)]
101+
public int ZLinq()
102+
{
103+
return _nums.AsValueEnumerable().Count(i => i <= _target);
104+
}
105+
106+
[Benchmark]
107+
[BenchmarkCategory(Categories.LINQ)]
108+
public int Linq()
109+
{
110+
return _nums.Count(i => i <= _target);
111+
}
112+
}

sandbox/Benchmark/Properties/launchSettings.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"profiles": {
33
"Default": {
44
"commandName": "Project",
5-
"commandLineArgs": "--filter Benchmark.AllPredicateWithCapturedLambda* -- Default",
5+
"commandLineArgs": "--filter Benchmark.CountPredicateWithCapturedLambda* -- Default",
66

77
// Available config
88
// 1. Default

src/ZLinq/Linq/Count.cs

+41
Original file line numberDiff line numberDiff line change
@@ -63,5 +63,46 @@ public static Int32 Count<TEnumerator, TSource>(this ValueEnumerable<TEnumerator
6363

6464
return count;
6565
}
66+
67+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
68+
public static Int32 Count<TSource>(this ValueEnumerable<FromArray<TSource>, TSource> source, Func<TSource, Boolean> predicate)
69+
{
70+
ArgumentNullException.ThrowIfNull(predicate);
71+
72+
var array = source.Enumerator.GetSource();
73+
if (array.GetType() != typeof(TSource[]))
74+
{
75+
return Count(array, predicate);
76+
}
77+
78+
var count = 0;
79+
80+
// The assembly output differs slightly when iterating through array and span.
81+
// According to microbenckmark results, iterating through span was faster for the logic passed to predicate.
82+
var span = (ReadOnlySpan<TSource>)array;
83+
for (int i = 0; i < span.Length; i++)
84+
{
85+
if (predicate(span[i]))
86+
{
87+
count++;
88+
}
89+
}
90+
91+
return count;
92+
93+
[MethodImpl(MethodImplOptions.NoInlining)]
94+
static int Count(TSource[] array, Func<TSource, Boolean> predicate)
95+
{
96+
var count = 0;
97+
for (int i = 0; i < array.Length; i++)
98+
{
99+
if (predicate(array[i]))
100+
{
101+
count++;
102+
}
103+
}
104+
return count;
105+
}
106+
}
66107
}
67108
}

0 commit comments

Comments
 (0)