-
Notifications
You must be signed in to change notification settings - Fork 5.2k
Description
Description
similar how Max.cs & Min try to convert to span
if (source.TryGetSpan(out ReadOnlySpan<T> span)) |
if (source.TryGetSpan(out ReadOnlySpan<decimal> span)) |
Iterating over a span when possible would be faster then iterating over enumerable.
Configuration
// * Summary *
BenchmarkDotNet v0.13.12, Windows 11 (10.0.22631.3593/23H2/2023Update/SunValley3)
11th Gen Intel Core i9-11950H 2.60GHz, 1 CPU, 16 logical and 8 physical cores
.NET SDK 8.0.205
[Host] : .NET 8.0.5 (8.0.524.21615), X64 RyuJIT AVX-512F+CD+BW+DQ+VL+VBMI
ShortRun : .NET 8.0.5 (8.0.524.21615), X64 RyuJIT AVX-512F+CD+BW+DQ+VL+VBMI
Job=ShortRun IterationCount=3 LaunchCount=1
WarmupCount=3
Method | count | Mean | Error | StdDev | Allocated |
---|---|---|---|---|---|
Count_IEnumerable | 100 | 1,732.7 ns | 741.6 ns | 40.65 ns | 48 B |
Count_List | 100 | 209.5 ns | 427.7 ns | 23.44 ns | 40 B |
Count_Array | 100 | 196.8 ns | 256.8 ns | 14.08 ns | 32 B |
Count_IEnumerable_New | 100 | 1,638.3 ns | 1,091.5 ns | 59.83 ns | 48 B |
Count_List_New | 100 | 164.6 ns | 366.9 ns | 20.11 ns | - |
Count_Array_New | 100 | 114.8 ns | 135.1 ns | 7.41 ns | - |
Count_IEnumerable | 10000 | 161,695.6 ns | 64,981.1 ns | 3,561.83 ns | 48 B |
Count_List | 10000 | 42,592.3 ns | 17,101.9 ns | 937.41 ns | 40 B |
Count_Array | 10000 | 44,711.1 ns | 15,163.0 ns | 831.14 ns | 32 B |
Count_IEnumerable_New | 10000 | 161,839.9 ns | 210,702.2 ns | 11,549.30 ns | 48 B |
Count_List_New | 10000 | 36,906.0 ns | 32,625.2 ns | 1,788.30 ns | - |
Count_Array_New | 10000 | 36,233.2 ns | 7,040.9 ns | 385.94 ns | - |
Count_IEnumerable | 1000000 | 14,854,601.0 ns | 12,265,409.7 ns | 672,308.49 ns | 60 B |
Count_List | 1000000 | 6,323,082.3 ns | 9,923,756.9 ns | 543,954.60 ns | 43 B |
Count_Array | 1000000 | 6,402,595.8 ns | 2,037,387.6 ns | 111,676.09 ns | 35 B |
Count_IEnumerable_New | 1000000 | 15,711,805.2 ns | 13,675,725.7 ns | 749,612.67 ns | 54 B |
Count_List_New | 1000000 | 4,253,217.7 ns | 3,217,607.6 ns | 176,367.93 ns | 3 B |
Count_Array_New | 1000000 | 4,241,221.4 ns | 4,164,944.1 ns | 228,294.64 ns | 3 B |
// * Legends *
count : Value of the 'count' parameter
Mean : Arithmetic mean of all measurements
Error : Half of 99.9% confidence interval
StdDev : Standard deviation of all measurements
Allocated : Allocated memory per single operation (managed only, inclusive, 1KB = 1024B)
1 ns : 1 Nanosecond (0.000000001 sec)
// * Diagnostic Output - MemoryDiagnoser *
// ***** BenchmarkRunner: End *****
Run time: 00:01:54 (114.04 sec), executed benchmarks: 18
Global total time: 00:02:04 (124.39 sec), executed benchmarks: 18
using System.Runtime.InteropServices;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
namespace MyBenchmarks;
[MemoryDiagnoser(false)]
[ShortRunJob]
public class EnumerableExcept
{
[Params(100, 10000, 1000000)]
public int count;
private IEnumerable<int> collection;
private List<int> list;
private int[] array;
[GlobalSetup]
public void GlobalSetup()
{
Random random = new Random(42);
collection = Enumerable.Range(0, count).Select(i => random.Next());
list = collection.ToList();
array = collection.ToArray();
}
[Benchmark]
public int Count_IEnumerable() => collection.Count(x => x % 2 == 0);
[Benchmark]
public int Count_List() => list.Count(x => x % 2 == 0);
[Benchmark]
public int Count_Array() => array.Count(x => x % 2 == 0);
[Benchmark]
public int Count_IEnumerable_New() => MyEnumerable.Count(collection, x => x % 2 == 0);
[Benchmark]
public int Count_List_New() => MyEnumerable.Count(list, x => x % 2 == 0);
[Benchmark]
public int Count_Array_New() => MyEnumerable.Count(array, x => x % 2 == 0);
}
public static class MyEnumerable
{
public static int Count<TSource>(IEnumerable<TSource> collection, Func<TSource, bool> predicate)
{
ArgumentNullException.ThrowIfNull(collection);
ArgumentNullException.ThrowIfNull(predicate);
if (collection is TSource[] array)
{
return SpanCount(array, predicate);
}
if (collection is List<TSource> list)
{
return SpanCount(CollectionsMarshal.AsSpan(list), predicate);
}
int count = 0;
foreach (var item in collection)
{
if (predicate(item))
{
count++;
}
}
return count;
static int SpanCount(Span<TSource> span, Func<TSource, bool> predicate)
{
int count = 0;
for (int i = 0; i < span.Length; i++)
{
if (predicate(span[i]))
{
count++;
}
}
return count;
}
}
}
public class Program
{
public static void Main(string[] args)
{
var summary = BenchmarkRunner.Run<EnumerableExcept>();
}
}
Regression?
No
Analysis
int count = 0; |
source.TryGetSpan(out ReadOnlySpan<TSource> span)
and then a dedicated foreach loop for the read-only-span