diff --git a/Benchmarks/Benchmarks/FirstOperatorBenchmark.cs b/Benchmarks/Benchmarks/FirstOperatorBenchmark.cs index 6d282e3..12dee8b 100644 --- a/Benchmarks/Benchmarks/FirstOperatorBenchmark.cs +++ b/Benchmarks/Benchmarks/FirstOperatorBenchmark.cs @@ -21,12 +21,18 @@ public void Setup() [Benchmark(Baseline = true)] public void FrozenSequenceFirst() { - _ = _frozenSequence!.First(number => number is 50); + for (var i = 0; i < 3; i++) + { + _ = _frozenSequence!.First(number => number is 50); + } } [Benchmark] public void ListFirst() { - _ = _list!.First(number => number is 50); + for (var i = 0; i < 3; i++) + { + _ = _list!.First(number => number is 50); + } } } diff --git a/Benchmarks/Benchmarks/LastOperatorBenchmark.cs b/Benchmarks/Benchmarks/LastOperatorBenchmark.cs new file mode 100644 index 0000000..fac46de --- /dev/null +++ b/Benchmarks/Benchmarks/LastOperatorBenchmark.cs @@ -0,0 +1,38 @@ +using BenchmarkDotNet.Attributes; +using Falko.Common.Extensions; +using Falko.Common.Sequences; + +namespace Benchmarks; + +public class LastOperatorBenchmark +{ + private List? _list; + + private FrozenSequence? _frozenSequence; + + [GlobalSetup] + public void Setup() + { + _frozenSequence = Enumerable.Range(0, 100).ToFrozenSequence(); + + _list = Enumerable.Range(0, 100).ToList(); + } + + [Benchmark(Baseline = true)] + public void FrozenSequenceFirst() + { + for (var i = 0; i < 3; i++) + { + _ = _frozenSequence!.Last(number => number is 50); + } + } + + [Benchmark] + public void ListFirst() + { + for (var i = 0; i < 3; i++) + { + _ = _list!.Last(number => number is 50); + } + } +} diff --git a/Benchmarks/Benchmarks/Program.cs b/Benchmarks/Benchmarks/Program.cs index 3201438..c05779b 100644 --- a/Benchmarks/Benchmarks/Program.cs +++ b/Benchmarks/Benchmarks/Program.cs @@ -1,8 +1,5 @@ using BenchmarkDotNet.Running; using Benchmarks; -var b = new FirstOperatorBenchmark(); -b.Setup(); -b.FrozenSequenceFirst(); -b.ListFirst(); BenchmarkRunner.Run(); +BenchmarkRunner.Run(); diff --git a/Falko.Common.slnx b/Falko.Common.slnx index 09dcf30..347def6 100644 --- a/Falko.Common.slnx +++ b/Falko.Common.slnx @@ -23,4 +23,4 @@ - + \ No newline at end of file diff --git a/Sources/Falko.Common.Sequences/Asserts/SequenceExceptions.cs b/Sources/Falko.Common.Sequences/Asserts/SequenceExceptions.cs index 0f6dadb..9309908 100644 --- a/Sources/Falko.Common.Sequences/Asserts/SequenceExceptions.cs +++ b/Sources/Falko.Common.Sequences/Asserts/SequenceExceptions.cs @@ -67,7 +67,6 @@ public static void ThrowIfNotSingle(int count) } [DoesNotReturn] - [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ThrowNotMatchAny() { throw new InvalidOperationException("The source sequence does not match any of the specified conditions."); diff --git a/Sources/Falko.Common.Sequences/Sequences/FrozenSequence.Operator.First.cs b/Sources/Falko.Common.Sequences/Sequences/FrozenSequence.Operator.First.cs index 74cb6a5..5ecea87 100644 --- a/Sources/Falko.Common.Sequences/Sequences/FrozenSequence.Operator.First.cs +++ b/Sources/Falko.Common.Sequences/Sequences/FrozenSequence.Operator.First.cs @@ -20,13 +20,19 @@ public T First(Func predicate) { ArgumentNullException.ThrowIfNull(predicate); - foreach (ref readonly var item in this) + var itemsCount = _itemsCount; + + scoped ref var itemsReference = ref MemoryMarshal.GetArrayDataReference(_items); + + for (var itemIndex = 0; itemIndex < itemsCount; itemIndex++) { + var item = Unsafe.Add(ref itemsReference, itemIndex); + if (predicate(item)) return item; } SequenceExceptions.ThrowNotMatchAny(); - return default!; // This line is unreachable + return default; // This line is unreachable } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -41,8 +47,14 @@ public T First(Func predicate) { ArgumentNullException.ThrowIfNull(predicate); - foreach (ref readonly var item in this) + var itemsCount = _itemsCount; + + scoped ref var itemsReference = ref MemoryMarshal.GetArrayDataReference(_items); + + for (var itemIndex = 0; itemIndex < itemsCount; itemIndex++) { + var item = Unsafe.Add(ref itemsReference, itemIndex); + if (predicate(item)) return item; } diff --git a/Sources/Falko.Common.Sequences/Sequences/FrozenSequence.Operator.Last.cs b/Sources/Falko.Common.Sequences/Sequences/FrozenSequence.Operator.Last.cs index 1da92e9..d628b83 100644 --- a/Sources/Falko.Common.Sequences/Sequences/FrozenSequence.Operator.Last.cs +++ b/Sources/Falko.Common.Sequences/Sequences/FrozenSequence.Operator.Last.cs @@ -19,12 +19,10 @@ public T Last() public T Last(Func predicate) { - var itemsCount = _itemsCount; - - SequenceExceptions.ThrowIfEmpty(itemsCount); - ArgumentNullException.ThrowIfNull(predicate); + var itemsCount = _itemsCount; + scoped ref var itemsReference = ref MemoryMarshal.GetArrayDataReference(_items); for (var itemIndex = itemsCount - 1; itemIndex >= 0; itemIndex--) @@ -50,12 +48,10 @@ public T Last(Func predicate) public T? LastOrDefault(Func predicate) { - var itemsCount = _itemsCount; - - if (itemsCount is 0) return default; - ArgumentNullException.ThrowIfNull(predicate); + var itemsCount = _itemsCount; + scoped ref var itemsReference = ref MemoryMarshal.GetArrayDataReference(_items); for (var itemIndex = itemsCount - 1; itemIndex >= 0; itemIndex--) diff --git a/Sources/Falko.Common.Sequences/Sequences/FrozenSequence.ValueEnumerator.cs b/Sources/Falko.Common.Sequences/Sequences/FrozenSequence.ValueEnumerator.cs index 7ce5afc..c39d887 100644 --- a/Sources/Falko.Common.Sequences/Sequences/FrozenSequence.ValueEnumerator.cs +++ b/Sources/Falko.Common.Sequences/Sequences/FrozenSequence.ValueEnumerator.cs @@ -16,6 +16,7 @@ public ref struct ValueEnumerator [MethodImpl(MethodImplOptions.AggressiveInlining)] internal ValueEnumerator(T[] values, int valuesCount) { + _currentIndex = -1; _valuesReference = ref MemoryMarshal.GetArrayDataReference(values); _valuesCount = valuesCount; } @@ -29,19 +30,18 @@ public ref readonly T Current [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool MoveNext() { - var currentIndex = _currentIndex; + var nextIndex = _currentIndex + 1; - if (currentIndex == _valuesCount) return false; - - _currentIndex = currentIndex + 1; + if (nextIndex >= _valuesCount) return false; + _currentIndex = nextIndex; return true; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Reset() { - _currentIndex = 0; + _currentIndex = -1; } } }