CyclicBuffer optimizations

This commit is contained in:
wreng 2024-02-18 20:37:11 +03:00 committed by Ikpil
parent 804cb275a7
commit 097a365528
3 changed files with 93 additions and 25 deletions

View File

@ -1,12 +1,47 @@
using System; using System;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Net.Security; using System.Net.Security;
namespace DotRecast.Core.Buffers namespace DotRecast.Core.Buffers
{ {
// https://github.com/joaoportela/CircularBuffer-CSharp/blob/master/CircularBuffer/CircularBuffer.cs // https://github.com/joaoportela/CircularBuffer-CSharp/blob/master/CircularBuffer/CircularBuffer.cs
public class RcCyclicBuffer<T> public class RcCyclicBuffer<T> : IEnumerable<T>
{ {
public struct Enumerator : IEnumerator<T>
{
private readonly RcCyclicBuffer<T> _buffer;
private int _index;
private readonly int _size;
internal Enumerator(RcCyclicBuffer<T> buffer)
{
_buffer = buffer;
_size = _buffer._size;
_index = default;
Reset();
}
public bool MoveNext()
{
return ++_index < _size;
}
public void Reset()
{
_index = -1;
}
public T Current => _buffer[_index];
object IEnumerator.Current => Current;
public void Dispose()
{
// This could be used to unlock write access to collection
}
}
private readonly T[] _buffer; private readonly T[] _buffer;
private int _start; private int _start;
@ -155,29 +190,15 @@ namespace DotRecast.Core.Buffers
public T[] ToArray() public T[] ToArray()
{ {
int idx = 0;
T[] newArray = new T[Size]; T[] newArray = new T[Size];
ForEach(x => newArray[idx++] = x); var span1 = ArrayOne();
span1.CopyTo(newArray.AsSpan());
ArrayTwo().CopyTo(newArray.AsSpan(span1.Length..));
return newArray; return newArray;
} }
public void ForEach(Action<T> action)
{
var spanOne = ArrayOne();
foreach (var item in spanOne)
{
action.Invoke(item);
}
var spanTwo = ArrayTwo();
foreach (var item in spanTwo)
{
action.Invoke(item);
}
}
private void ThrowIfEmpty(string message = "Cannot access an empty buffer.") private void ThrowIfEmpty(string message = "Cannot access an empty buffer.")
{ {
if (IsEmpty) if (IsEmpty)
@ -240,5 +261,11 @@ namespace DotRecast.Core.Buffers
return new Span<T>(_buffer, 0, _end); return new Span<T>(_buffer, 0, _end);
} }
public Enumerator GetEnumerator() => new Enumerator(this);
IEnumerator<T> IEnumerable<T>.GetEnumerator() => GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
} }
} }

View File

@ -7,7 +7,11 @@
long sum = 0; long sum = 0;
checked checked
{ {
source.ForEach(x => sum += x); // NOTE: SIMD would be nice here
foreach (var x in source)
{
sum += x;
}
} }
return sum; return sum;
@ -27,11 +31,11 @@
return 0; return 0;
long minValue = long.MaxValue; long minValue = long.MaxValue;
source.ForEach(x => foreach (var x in source)
{ {
if (x < minValue) if (x < minValue)
minValue = x; minValue = x;
}); }
return minValue; return minValue;
} }
@ -42,11 +46,11 @@
return 0; return 0;
long maxValue = long.MinValue; long maxValue = long.MinValue;
source.ForEach(x => foreach (var x in source)
{ {
if (x > maxValue) if (x > maxValue)
maxValue = x; maxValue = x;
}); }
return maxValue; return maxValue;
} }

View File

@ -1,5 +1,6 @@
using System; using System;
using DotRecast.Core.Buffers; using DotRecast.Core.Buffers;
using DotRecast.Core.Collections;
using NUnit.Framework; using NUnit.Framework;
namespace DotRecast.Core.Test; namespace DotRecast.Core.Test;
@ -39,11 +40,11 @@ public class RcCyclicBufferTests
var buffer = new RcCyclicBuffer<int>(5, new[] { 0, 1, 2, 3 }); var buffer = new RcCyclicBuffer<int>(5, new[] { 0, 1, 2, 3 });
int x = 0; int x = 0;
buffer.ForEach(item => foreach (var item in buffer)
{ {
Assert.That(item, Is.EqualTo(x)); Assert.That(item, Is.EqualTo(x));
x++; x++;
}); }
} }
[Test] [Test]
@ -290,4 +291,40 @@ public class RcCyclicBufferTests
Assert.That(buffer[i], Is.EqualTo(i)); Assert.That(buffer[i], Is.EqualTo(i));
} }
} }
[Test]
public void RcCyclicBuffer_RegularForEachWorks()
{
var refValues = new[] { 4, 3, 2, 1, 0 };
var buffer = new RcCyclicBuffer<int>(5, refValues);
var index = 0;
foreach (var element in buffer)
{
Assert.That(element, Is.EqualTo(refValues[index++]));
}
}
[Test]
public void RcCyclicBuffer_EnumeratorWorks()
{
var refValues = new[] { 4, 3, 2, 1, 0 };
var buffer = new RcCyclicBuffer<int>(5, refValues);
var index = 0;
var enumerator = buffer.GetEnumerator();
enumerator.Reset();
while (enumerator.MoveNext())
{
Assert.That(enumerator.Current, Is.EqualTo(refValues[index++]));
}
// Ensure Reset works properly
index = 0;
enumerator.Reset();
while (enumerator.MoveNext())
{
Assert.That(enumerator.Current, Is.EqualTo(refValues[index++]));
}
}
} }