Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a few struct promotion related benchmarks #2991

Merged
merged 1 commit into from
Apr 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
68 changes: 68 additions & 0 deletions src/benchmarks/micro/runtime/Struct/FilteredSpanEnumerator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using BenchmarkDotNet.Attributes;
using MicroBenchmarks;
using System;
using System.Linq;
using System.Runtime.CompilerServices;

namespace Struct
{
[BenchmarkCategory(Categories.Runtime, Categories.JIT)]
public class FilteredSpanEnumerator
{
private int[] _array;
private int[] _inds;

[GlobalSetup]
public void Setup()
{
_array = Enumerable.Range(0, 10000).ToArray();
_inds = Enumerable.Range(0, 10000).ToArray();
}

[Benchmark]
public int Sum()
{
int sum = 0;
foreach (int s in new FilteredSpanEnumerator<int>(_array, _inds))
{
sum += s;
}

return sum;
}
}

public ref struct FilteredSpanEnumerator<T>
{
private readonly ReadOnlySpan<T> arr;
private readonly int[] inds;

private T current;
private int i;

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public FilteredSpanEnumerator(ReadOnlySpan<T> arr, int[] inds) {
this.arr = arr;
this.inds = inds;
current = default;
i = 0;
}

public T Current => current;

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool MoveNext() {
if (i >= inds.Length)
return false;

current = arr[inds[i++]];
return true;
}

public FilteredSpanEnumerator<T> GetEnumerator() => this;
}
}
215 changes: 215 additions & 0 deletions src/benchmarks/micro/runtime/Struct/GSeq.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using BenchmarkDotNet.Attributes;
using MicroBenchmarks;
using System.Collections.Generic;
using System.Collections;
using System.Linq;

namespace Struct
{
[BenchmarkCategory(Categories.Runtime, Categories.JIT)]
public class GSeq
{
private int[] _array;

[GlobalSetup]
public void Setup()
{
_array = Enumerable.Range(0, 10000).ToArray();
}

[Benchmark]
public int FilterSkipMapSum()
{
var arr = new ArrayEnumerator<int>(_array);
var filter = new FilterEnumerator<int, ArrayEnumerator<int>, FilterIsOdd>(arr, new FilterIsOdd());
var skip = new SkipEnumerator<int, FilterEnumerator<int, ArrayEnumerator<int>, FilterIsOdd>>(5, filter);
var map =
new MapEnumerator<int, int,
SkipEnumerator<int, FilterEnumerator<int, ArrayEnumerator<int>, FilterIsOdd>>, Plus15Mapper>(
skip,
new Plus15Mapper());
return Fold2<int, int,
MapEnumerator<int, int,
SkipEnumerator<int, FilterEnumerator<int, ArrayEnumerator<int>, FilterIsOdd>>,
Plus15Mapper>, FoldPlus>(map, 0, new FoldPlus());
}

public static TOut Fold2<TIn, TOut, TEnumerator, TFolder>(TEnumerator enumerator, TOut initial,
TFolder folder)
where TEnumerator : struct, IEnumerator<TIn>
where TFolder : struct, IInvokable<TOut, TIn, TOut>
{
while (enumerator.MoveNext())
initial = folder.Invoke(initial, enumerator.Current);

return initial;
}
}

public interface IInvokable<TIn1, TOut>
{
TOut Invoke(TIn1 value);
}

public interface IInvokable<TIn1, TIn2, TOut>
{
TOut Invoke(TIn1 value1, TIn2 value2);
}


public readonly struct Plus15Mapper : IInvokable<int, int>
{
public int Invoke(int value) => value + 15;
}

public readonly struct FilterIsOdd : IInvokable<int, bool>
{
public bool Invoke(int value) => value % 2 == 1;
}

public readonly struct FoldPlus : IInvokable<int, int, int>
{
public int Invoke(int value1, int value2) => value1 + value2;
}

public struct ArrayEnumerator<T> : IEnumerator<T>
{
private readonly T[] _array;
private int _count;

public ArrayEnumerator(T[] array)
{
_array = array;
_count = -1;
}


public bool MoveNext()
{
_count++;
return (uint)_count < (uint)_array.Length;
}

public void Reset()
{
}

public T Current => _array[_count];

object IEnumerator.Current => Current;

public void Dispose()
{
}
}

public struct SkipEnumerator<T, TEnumerator> : IEnumerator<T>
where TEnumerator : struct, IEnumerator<T>
{
private TEnumerator _enumerator;
private int _skipCount;

public SkipEnumerator(int skipCount, TEnumerator enumerator)
{
_skipCount = skipCount;
_enumerator = enumerator;
}

public bool MoveNext()
{
while (_skipCount > 0)
{
if (!_enumerator.MoveNext())
return false;
_skipCount--;
}

return _enumerator.MoveNext();
}

public void Reset()
{
}

public T Current => _enumerator.Current;

object IEnumerator.Current => Current;

public void Dispose()
{
}
}

public struct MapEnumerator<T, TOut, TEnumerator, TMapper> : IEnumerator<TOut>
where TEnumerator : struct, IEnumerator<T>
where TMapper : struct, IInvokable<T, TOut>
{
private TEnumerator _enumerator;
private TMapper _mapper;

public MapEnumerator(TEnumerator enumerator, TMapper mapper)
{
_enumerator = enumerator;
_mapper = mapper;
}

public bool MoveNext()
{
return _enumerator.MoveNext();
}

public void Reset()
{
}

public TOut Current => _mapper.Invoke(_enumerator.Current);

object IEnumerator.Current => Current;

public void Dispose()
{
}
}

public struct FilterEnumerator<T, TEnumerator, TFilter> : IEnumerator<T>
where TEnumerator : struct, IEnumerator<T>
where TFilter : struct, IInvokable<T, bool>
{
private TEnumerator _enumerator;
private TFilter _filter;

public FilterEnumerator(TEnumerator enumerator, TFilter filter)
{
_enumerator = enumerator;
_filter = filter;
}

public bool MoveNext()
{
while (_enumerator.MoveNext())
{
if (_filter.Invoke(_enumerator.Current))
return true;
}

return false;
}

public void Reset()
{
}

public T Current => _enumerator.Current;

object IEnumerator.Current => Current;

public void Dispose()
{
}
}

}
62 changes: 62 additions & 0 deletions src/benchmarks/micro/runtime/Struct/SpanWrapper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using BenchmarkDotNet.Attributes;
using MicroBenchmarks;
using System.Linq;
using System;
using System.Runtime.CompilerServices;

namespace Struct
{
[BenchmarkCategory(Categories.Runtime, Categories.JIT)]
public class SpanWrapper
{
private int[] _array;

[GlobalSetup]
public void Setup()
{
_array = Enumerable.Range(0, 10000).ToArray();
}

[Benchmark]
public int BaselineSum()
{
return SumSpan(_array);
}

[Benchmark]
public int WrapperSum()
{
return SumSpanWrapper(new SpanWrapper<int> { Span = _array });
}

[MethodImpl(MethodImplOptions.NoInlining)]
private int SumSpan(ReadOnlySpan<int> span)
{
int sum = 0;
foreach (int val in span)
sum += val;

return sum;
}

[MethodImpl(MethodImplOptions.NoInlining)]
private int SumSpanWrapper(SpanWrapper<int> spanWrapper)
{
int sum = 0;
foreach (int val in spanWrapper)
sum += val;

return sum;
}
}

public ref struct SpanWrapper<T>
{
public ReadOnlySpan<T> Span;
public ReadOnlySpan<T>.Enumerator GetEnumerator() => Span.GetEnumerator();
}
}