Skip to content

Instantly share code, notes, and snippets.

@aalmada
Last active February 26, 2024 15:53
Show Gist options
  • Save aalmada/7ebf12fcde1e4ec3e86fdfc3dbf705b2 to your computer and use it in GitHub Desktop.
Save aalmada/7ebf12fcde1e4ec3e86fdfc3dbf705b2 to your computer and use it in GitHub Desktop.
using BenchmarkDotNet.Columns;
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Reports;
namespace DotNetBenchmarks;
class BaseConfig : ManualConfig
{
public BaseConfig()
{
_ = WithSummaryStyle(SummaryStyle.Default.WithRatioStyle(RatioStyle.Trend));
_ = HideColumns(Column.EnvironmentVariables, Column.RatioSD, Column.Error);
}
}
using BenchmarkDotNet.Attributes;
using System;
using System.Linq;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace DotNetBenchmarks;
[Config(typeof(BaseConfig))]
public class GetFirstPositiveBenchmarks
{
List<int>? list;
[Params(1000)]
public int Count { get; set; }
[GlobalSetup]
public void GlobalSetup()
{
list = Enumerable.Range(-Count + 5, Count).ToList();
}
[Benchmark(Baseline = true)]
public void Enumerable_FirstOrDefault()
=> GetFirstPositive(list!.AsEnumerable());
[Benchmark]
public void Enumerable_Foreach()
=> GetFirstPositive2(list!.AsEnumerable());
[Benchmark]
public void List_Find()
=> GetFirstPositive(list!);
[Benchmark]
public void List_ForEach()
=> GetFirstPositive2(list!);
static int GetFirstPositive(IEnumerable<int> source)
=> source.FirstOrDefault(i => i > 0);
static int GetFirstPositive2(IEnumerable<int> source)
{
foreach (var item in source)
{
if (item > 0)
return item;
}
return default;
}
static int GetFirstPositive(List<int> source)
=> source.Find(i => i > 0);
static int GetFirstPositive2(List<int> source)
=> GetFirstPositive(CollectionsMarshal.AsSpan(source));
static int GetFirstPositive(ReadOnlySpan<int> source)
{
var indexSource = nint.Zero;
if (Vector.IsHardwareAccelerated)
{
var vectors = MemoryMarshal.Cast<int, Vector<int>>(source);
ref var vectorsRef = ref MemoryMarshal.GetReference(vectors);
var zeroVector = Vector<int>.Zero;
var indexVector = nint.Zero;
for (; indexVector < vectors.Length; indexVector++)
{
ref var currentVector = ref Unsafe.Add(ref vectorsRef, indexVector);
if (Vector.GreaterThanAny(currentVector, zeroVector))
{
for (var index = 0; index < Vector<int>.Count; index++)
{
if (currentVector.GetElement(index) > 0)
return currentVector.GetElement(index);
}
}
}
indexSource = indexVector * Vector<int>.Count;
}
ref var sourceRef = ref MemoryMarshal.GetReference(source);
for (; indexSource < source.Length; indexSource++)
{
if (Unsafe.Add(ref sourceRef, indexSource) > 0)
return Unsafe.Add(ref sourceRef, indexSource);
}
return default;
}
}

BenchmarkDotNet v0.13.12, Windows 10 (10.0.19045.4116/22H2/2022Update)
Intel Core i7-7567U CPU 3.50GHz (Kaby Lake), 1 CPU, 4 logical and 2 physical cores
.NET SDK 9.0.100-preview.1.24101.2
  [Host]     : .NET 8.0.2 (8.0.224.6711), X64 RyuJIT AVX2
  DefaultJob : .NET 8.0.2 (8.0.224.6711), X64 RyuJIT AVX2

EnvironmentVariables=Empty  

| Method | Count | Mean | StdDev | Median | Ratio | |-------------------------- |------ |-----------:|----------:|-----------:|--------------:|- | Enumerable_FirstOrDefault | 1000 | 2,019.1 ns | 171.60 ns | 1,979.6 ns | baseline | | Enumerable_Foreach | 1000 | 1,763.0 ns | 65.06 ns | 1,746.2 ns | 1.15x faster | | List_Find | 1000 | 1,115.2 ns | 76.84 ns | 1,086.5 ns | 1.82x faster | | List_ForEach | 1000 | 121.3 ns | 7.93 ns | 118.2 ns | 16.74x faster |

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment