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.Collections;
using System.Collections.Generic;
using System.Net.Security;
namespace DotRecast.Core.Buffers
{
// 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 int _start;
@ -155,29 +190,15 @@ namespace DotRecast.Core.Buffers
public T[] ToArray()
{
int idx = 0;
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;
}
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.")
{
if (IsEmpty)
@ -240,5 +261,11 @@ namespace DotRecast.Core.Buffers
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;
checked
{
source.ForEach(x => sum += x);
// NOTE: SIMD would be nice here
foreach (var x in source)
{
sum += x;
}
}
return sum;
@ -27,11 +31,11 @@
return 0;
long minValue = long.MaxValue;
source.ForEach(x =>
foreach (var x in source)
{
if (x < minValue)
minValue = x;
});
}
return minValue;
}
@ -42,11 +46,11 @@
return 0;
long maxValue = long.MinValue;
source.ForEach(x =>
foreach (var x in source)
{
if (x > maxValue)
maxValue = x;
});
}
return maxValue;
}

View File

@ -1,5 +1,6 @@
using System;
using DotRecast.Core.Buffers;
using DotRecast.Core.Collections;
using NUnit.Framework;
namespace DotRecast.Core.Test;
@ -39,11 +40,11 @@ public class RcCyclicBufferTests
var buffer = new RcCyclicBuffer<int>(5, new[] { 0, 1, 2, 3 });
int x = 0;
buffer.ForEach(item =>
foreach (var item in buffer)
{
Assert.That(item, Is.EqualTo(x));
x++;
});
}
}
[Test]
@ -290,4 +291,40 @@ public class RcCyclicBufferTests
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++]));
}
}
}