Skip to content

Commit

Permalink
CyclicBuffer optimizations
Browse files Browse the repository at this point in the history
  • Loading branch information
wrenge authored and ikpil committed Feb 19, 2024
1 parent 804cb27 commit 097a365
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 25 deletions.
63 changes: 45 additions & 18 deletions src/DotRecast.Core/Buffers/RcCyclicBuffer.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -155,29 +190,15 @@ public void Clear()

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)
Expand Down Expand Up @@ -240,5 +261,11 @@ private Span<T> ArrayTwo()

return new Span<T>(_buffer, 0, _end);
}

public Enumerator GetEnumerator() => new Enumerator(this);

IEnumerator<T> IEnumerable<T>.GetEnumerator() => GetEnumerator();

IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
}
14 changes: 9 additions & 5 deletions src/DotRecast.Core/Buffers/RcCyclicBuffers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ public static long Sum(this RcCyclicBuffer<long> source)
long sum = 0;
checked
{
source.ForEach(x => sum += x);
// NOTE: SIMD would be nice here
foreach (var x in source)
{
sum += x;
}
}

return sum;
Expand All @@ -27,11 +31,11 @@ public static long Min(this RcCyclicBuffer<long> source)
return 0;

long minValue = long.MaxValue;
source.ForEach(x =>
foreach (var x in source)
{
if (x < minValue)
minValue = x;
});
}

return minValue;
}
Expand All @@ -42,11 +46,11 @@ public static long Max(this RcCyclicBuffer<long> source)
return 0;

long maxValue = long.MinValue;
source.ForEach(x =>
foreach (var x in source)
{
if (x > maxValue)
maxValue = x;
});
}

return maxValue;
}
Expand Down
41 changes: 39 additions & 2 deletions test/DotRecast.Core.Test/RcCyclicBufferTest.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using DotRecast.Core.Buffers;
using DotRecast.Core.Collections;
using NUnit.Framework;

namespace DotRecast.Core.Test;
Expand Down Expand Up @@ -39,11 +40,11 @@ public void RcCyclicBuffer_GetEnumeratorConstructorDefinedArray_CorrectContent()
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]
Expand Down Expand Up @@ -290,4 +291,40 @@ public void RcCyclicBuffer_Clear_WorksNormallyAfterClear()
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++]));
}
}
}

0 comments on commit 097a365

Please sign in to comment.