Created
February 16, 2023 19:45
-
-
Save Stroniax/145d2e87836604a40fc40195872245b9 to your computer and use it in GitHub Desktop.
SingleCollection Benchmark
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using BenchmarkDotNet.Attributes; | |
using BenchmarkDotNet.Running; | |
using System.Collections; | |
using System.Runtime.CompilerServices; | |
using System.Runtime.InteropServices; | |
namespace SingleCollectionBenchmark; | |
/// <summary> | |
/// <code> | |
/// BenchmarkDotNet=v0.13.4, OS=Windows 10 (10.0.19044.2486/21H2/November2021Update) | |
/// Intel Core i5-7500 CPU 3.40GHz(Kaby Lake), 1 CPU, 4 logical and 4 physical cores | |
/// .NET SDK= 7.0.102 | |
/// [Host] : .NET 7.0.2 (7.0.222.60605), X64 RyuJIT AVX2[AttachedDebugger] | |
/// DefaultJob : .NET 7.0.2 (7.0.222.60605), X64 RyuJIT AVX2 | |
/// | |
/// | Method | Mean | Error | StdDev | Ratio | RatioSD | Gen0 | Allocated | Alloc Ratio | | |
/// |------------------------------------------------- |---------:|---------:|---------:|------:|--------:|-------:|----------:|------------:| | |
/// | Array | 21.27 ns | 0.100 ns | 0.088 ns | 1.00 | 0.00 | 0.0204 | 64 B | 1.00 | | |
/// | List | 43.40 ns | 0.883 ns | 1.084 ns | 2.03 | 0.05 | 0.0408 | 128 B | 2.00 | | |
/// | Yield | 19.37 ns | 0.110 ns | 0.086 ns | 0.91 | 0.01 | 0.0153 | 48 B | 0.75 | | |
/// | SingleCollection | 17.41 ns | 0.250 ns | 0.222 ns | 0.82 | 0.01 | 0.0179 | 56 B | 0.88 | | |
/// | SingleCollectionAndEnumerator | 14.04 ns | 0.224 ns | 0.198 ns | 0.66 | 0.01 | 0.0102 | 32 B | 0.50 | | |
/// | SingleCollectionValueEnumerator | 24.98 ns | 0.128 ns | 0.107 ns | 1.18 | 0.01 | 0.0179 | 56 B | 0.88 | | |
/// | ValueSingleCollection | 27.51 ns | 0.579 ns | 0.541 ns | 1.29 | 0.03 | 0.0179 | 56 B | 0.88 | | |
/// | ValueSingleCollectionReferenceEnumerator | 19.34 ns | 0.186 ns | 0.156 ns | 0.91 | 0.01 | 0.0179 | 56 B | 0.88 | | |
/// | ReadonlyValueSingleCollection | 25.99 ns | 0.070 ns | 0.062 ns | 1.22 | 0.01 | 0.0179 | 56 B | 0.88 | | |
/// | ReadonlyValueSingleCollectionReferenceEnumerator | 20.31 ns | 0.410 ns | 0.403 ns | 0.95 | 0.02 | 0.0179 | 56 B | 0.88 | | |
/// </code> | |
/// </summary> | |
[MemoryDiagnoser] | |
public class Program | |
{ | |
public static void Main() | |
{ | |
BenchmarkRunner.Run<Program>(); | |
} | |
[Benchmark(Baseline = true)] | |
public double Array() => Calculation(FromArray(1.23)); | |
[Benchmark] | |
public double List() => Calculation(FromList(1.23)); | |
[Benchmark] | |
public double Yield() => Calculation(FromYield(1.23)); | |
[Benchmark] | |
public double SingleCollection() => Calculation(FromSingleCollection(1.23)); | |
[Benchmark] | |
public double SingleCollectionAndEnumerator() => Calculation(FromSingleCollectionAndEnumerator(1.23)); | |
[Benchmark] | |
public double SingleCollectionValueEnumerator() => Calculation(FromSingleCollectionValueEnumerator(1.23)); | |
[Benchmark] | |
public double ValueSingleCollection() => Calculation(FromValueSingleCollection(1.23)); | |
[Benchmark] | |
public double ValueSingleCollectionReferenceEnumerator() => Calculation(FromValueSingleCollectionReferenceEnumerator(1.23)); | |
[Benchmark] | |
public double ReadonlyValueSingleCollection() => Calculation(FromReadonlyValueSingleCollection(1.23)); | |
[Benchmark] | |
public double ReadonlyValueSingleCollectionReferenceEnumerator() => Calculation(FromReadonlyValueSingleCollectionReferenceEnumerator(1.23)); | |
private static double Calculation(IEnumerable<double> values) | |
{ | |
var result = 0.0; | |
foreach (var value in values) | |
{ | |
result += value * value; | |
} | |
return result; | |
} | |
private static IEnumerable<T> FromArray<T>(T item) => new T[] { item }; | |
private static IEnumerable<T> FromList<T>(T item) => new List<T>() { item }; | |
private static IEnumerable<T> FromYield<T>(T item) | |
{ | |
yield return item; | |
} | |
private static IEnumerable<T> FromSingleCollection<T>(T item) => new SingleCollection<T>(item); | |
private static IEnumerable<T> FromSingleCollectionValueEnumerator<T>(T item) => new SingleCollectionValueEnumerator<T>(item); | |
private static IEnumerable<T> FromValueSingleCollection<T>(T item) => new ValueSingleCollection<T>(item); | |
private static IEnumerable<T> FromValueSingleCollectionReferenceEnumerator<T>(T item) => new ValueSingleCollectionReferenceEnumerator<T>(item); | |
private static IEnumerable<T> FromReadonlyValueSingleCollection<T>(T item) => new ReadonlyValueSingleCollection<T>(item); | |
private static IEnumerable<T> FromReadonlyValueSingleCollectionReferenceEnumerator<T>(T item) => new ReadonlyValueSingleCollectionReferenceEnumerator<T>(item); | |
private static IEnumerable<T> FromSingleCollectionAndEnumerator<T>(T item) => new SingleCollectionAndEnumerator<T>(item); | |
} | |
/// <summary> | |
/// Value type enumerator for a single element. | |
/// </summary> | |
/// <typeparam name="T"></typeparam> | |
public struct ValueSingleEnumerator<T> : IEnumerator<T> | |
{ | |
public ValueSingleEnumerator(T value) => _value = value; | |
private readonly T _value; | |
private bool _position; | |
public T Current => _position ? _value : throw new InvalidOperationException(); | |
object? IEnumerator.Current => Current; | |
public bool MoveNext() | |
{ | |
if (_position) | |
{ | |
return false; | |
} | |
return _position = true; | |
} | |
public void Dispose() { } | |
public void Reset() => _position = false; | |
} | |
/// <summary> | |
/// Reference type enumerator for a single element. | |
/// </summary> | |
/// <typeparam name="T"></typeparam> | |
public sealed class SingleEnumerator<T> : IEnumerator<T> | |
{ | |
public SingleEnumerator(T value) => _value = value; | |
private readonly T _value; | |
private bool _position; | |
public T Current => _position ? _value : throw new InvalidOperationException(); | |
object? IEnumerator.Current => Current; | |
public bool MoveNext() | |
{ | |
if (_position) | |
{ | |
return false; | |
} | |
return _position = true; | |
} | |
public void Dispose() { } | |
public void Reset() => _position = false; | |
} | |
/// <summary> | |
/// Collection of one item. This <see cref="IEnumerable{T}"/> is implemented | |
/// as a reference type with a reference type enumerator. | |
/// </summary> | |
/// <typeparam name="T">The element type of the collection.</typeparam> | |
public sealed class SingleCollection<T> : IEnumerable<T> | |
{ | |
public SingleCollection(T value) => Value = value; | |
public readonly T Value; | |
public IEnumerator<T> GetEnumerator() => new SingleEnumerator<T>(Value); | |
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); | |
} | |
/// <summary> | |
/// Collection of one item. This <see cref="IEnumerable{T}"/> is implemented | |
/// as a reference type with a value type enumerator. | |
/// </summary> | |
/// <typeparam name="T">The element type of the collection.</typeparam> | |
public sealed class SingleCollectionValueEnumerator<T> : IEnumerable<T> | |
{ | |
public SingleCollectionValueEnumerator(T value) => Value = value; | |
public readonly T Value; | |
public IEnumerator<T> GetEnumerator() => new ValueSingleEnumerator<T>(Value); | |
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); | |
} | |
/// <summary> | |
/// Collection of one item. This <see cref="IEnumerable{T}"/> is implemented | |
/// as a value type with a value type enumerator. | |
/// </summary> | |
/// <typeparam name="T">The element type of the collection.</typeparam> | |
public struct ValueSingleCollection<T> : IEnumerable<T> | |
{ | |
public ValueSingleCollection(T value) => Value = value; | |
public readonly T Value; | |
public IEnumerator<T> GetEnumerator() => new ValueSingleEnumerator<T>(Value); | |
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); | |
} | |
/// <summary> | |
/// Collection of one item. This <see cref="IEnumerable{T}"/> is implemented | |
/// as a value type with a value type enumerator. | |
/// <br/> | |
/// This type is marked as <see langword="readonly"/> which causes a defensive | |
/// copy to be made and is bad for performance when we know the type will | |
/// always be cast to <see cref="IEnumerable{T}"/>. | |
/// </summary> | |
/// <typeparam name="T">The element type of the collection.</typeparam> | |
public readonly struct ReadonlyValueSingleCollection<T> : IEnumerable<T> | |
{ | |
public ReadonlyValueSingleCollection(T value) => Value = value; | |
public readonly T Value; | |
public IEnumerator<T> GetEnumerator() => new ValueSingleEnumerator<T>(Value); | |
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); | |
} | |
/// <summary> | |
/// Collection of one item. This <see cref="IEnumerable{T}"/> is implemented | |
/// as a reference type with a reference type enumerator. | |
/// <br/> | |
/// This type is marked as <see langword="readonly"/> which causes a defensive | |
/// copy to be made and is bad for performance when we know the type will | |
/// always be cast to <see cref="IEnumerable{T}"/>. | |
/// </summary> | |
/// <typeparam name="T">The element type of the collection.</typeparam> | |
public readonly struct ReadonlyValueSingleCollectionReferenceEnumerator<T> : IEnumerable<T> | |
{ | |
public ReadonlyValueSingleCollectionReferenceEnumerator(T value) => Value = value; | |
public readonly T Value; | |
public IEnumerator<T> GetEnumerator() => new SingleEnumerator<T>(Value); | |
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); | |
} | |
/// <summary> | |
/// Collection of one item. This <see cref="IEnumerable{T}"/> is implemented | |
/// as a reference type with a reference type enumerator. | |
/// </summary> | |
/// <typeparam name="T">The element type of the collection.</typeparam> | |
public struct ValueSingleCollectionReferenceEnumerator<T> : IEnumerable<T> | |
{ | |
public ValueSingleCollectionReferenceEnumerator(T value) => Value = value; | |
public readonly T Value; | |
public IEnumerator<T> GetEnumerator() => new SingleEnumerator<T>(Value); | |
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); | |
} | |
public sealed class SingleCollectionAndEnumerator<T> : IEnumerable<T>, IEnumerator<T> | |
{ | |
public SingleCollectionAndEnumerator(T value) => Value = value; | |
public readonly T Value; | |
private byte _state; | |
public T Current => Value; | |
object? IEnumerator.Current => Current; | |
public bool MoveNext() | |
{ | |
if (_state == 0) | |
{ | |
_state = 1; | |
return true; | |
} | |
else if (_state == 1) | |
{ | |
_state = byte.MaxValue; | |
return false; | |
} | |
else | |
{ | |
throw new InvalidOperationException(); | |
} | |
} | |
public IEnumerator<T> GetEnumerator() => this; | |
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); | |
public void Reset() => _state = 0; | |
public void Dispose() { } | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment