CyclicBuffers SIMD

This commit is contained in:
wreng 2024-02-21 16:06:00 +03:00 committed by Ikpil
parent fa837a84ed
commit c47cc79552
3 changed files with 128 additions and 26 deletions

View File

@ -262,6 +262,11 @@ namespace DotRecast.Core.Buffers
return new Span<T>(_buffer, 0, _end);
}
internal ReadOnlySpan<T> GetBufferSpan()
{
return _buffer;
}
public Enumerator GetEnumerator() => new Enumerator(this);
IEnumerator<T> IEnumerable<T>.GetEnumerator() => GetEnumerator();

View File

@ -1,20 +1,31 @@
namespace DotRecast.Core.Buffers
using System;
using System.Numerics;
using System.Runtime.InteropServices;
namespace DotRecast.Core.Buffers
{
public static class RcCyclicBuffers
{
public static long Sum(this RcCyclicBuffer<long> source)
{
long sum = 0;
checked
var buffer = source.GetBufferSpan();
var result = 0L;
if (Vector.IsHardwareAccelerated)
{
// NOTE: SIMD would be nice here
foreach (var x in source)
{
sum += x;
}
var vectors = MemoryMarshal.Cast<long, Vector<long>>(buffer);
var vecSum = Vector<long>.Zero;
foreach (var vec in vectors)
vecSum += vec;
result = Vector.Dot(vecSum, Vector<long>.One);
var remainder = source.Size % Vector<long>.Count;
buffer = buffer[^remainder..];
}
return sum;
foreach (var val in buffer)
result += val;
return result;
}
public static double Average(this RcCyclicBuffer<long> source)
@ -27,32 +38,54 @@
public static long Min(this RcCyclicBuffer<long> source)
{
if (0 >= source.Size)
return 0;
var buffer = source.GetBufferSpan();
var result = long.MaxValue;
long minValue = long.MaxValue;
foreach (var x in source)
if (Vector.IsHardwareAccelerated)
{
if (x < minValue)
minValue = x;
var vectors = MemoryMarshal.Cast<long, Vector<long>>(buffer);
var vecMin = Vector<long>.One * result;
foreach (var vec in vectors)
vecMin = Vector.Min(vecMin, vec);
for (int i = 0; i < Vector<long>.Count; i++)
result = Math.Min(result, vecMin[i]);
var remainder = source.Size % Vector<long>.Count;
buffer = buffer[^remainder..];
}
return minValue;
foreach (var val in buffer)
result = Math.Min(result, val);
return result;
}
public static long Max(this RcCyclicBuffer<long> source)
{
if (0 >= source.Size)
return 0;
var buffer = source.GetBufferSpan();
var result = long.MinValue;
long maxValue = long.MinValue;
foreach (var x in source)
if (Vector.IsHardwareAccelerated)
{
if (x > maxValue)
maxValue = x;
var vectors = MemoryMarshal.Cast<long, Vector<long>>(buffer);
var vecMax = Vector<long>.One * result;
foreach (var vec in vectors)
vecMax = Vector.Max(vecMax, vec);
for (int i = 0; i < Vector<long>.Count; i++)
result = Math.Max(result, vecMax[i]);
var remainder = source.Size % Vector<long>.Count;
buffer = buffer[^remainder..];
}
return maxValue;
foreach (var val in buffer)
result = Math.Max(result, val);
return result;
}
}
}

View File

@ -330,4 +330,68 @@ public class RcCyclicBufferTests
Assert.That(enumerator.Current, Is.EqualTo(refValues[index++]));
}
}
[Test]
public void RcCyclicBuffers_Sum()
{
var refValues = Enumerable.Range(-100, 211).Select(x => (long)x).ToArray();
var buffer = new RcCyclicBuffer<long>(refValues.Length, refValues);
Assert.That(RcCyclicBuffers.Sum(buffer), Is.EqualTo(refValues.Sum()));
}
[Test]
public void RcCyclicBuffers_Average()
{
var refValues = Enumerable.Range(-100, 211).Select(x => (long)x).ToArray();
var buffer = new RcCyclicBuffer<long>(refValues.Length, refValues);
Assert.That(RcCyclicBuffers.Average(buffer), Is.EqualTo(refValues.Average()));
}
[Test]
public void RcCyclicBuffers_Min()
{
var refValues = Enumerable.Range(-100, 211).Select(x => (long)x).ToArray();
var buffer = new RcCyclicBuffer<long>(refValues.Length, refValues);
Assert.That(RcCyclicBuffers.Min(buffer), Is.EqualTo(refValues.Min()));
}
[Test]
public void RcCyclicBuffers_Max()
{
var refValues = Enumerable.Range(-100, 211).Select(x => (long)x).ToArray();
var buffer = new RcCyclicBuffer<long>(refValues.Length, refValues);
Assert.That(RcCyclicBuffers.Max(buffer), Is.EqualTo(refValues.Max()));
}
[Test]
public void RcCyclicBuffers_SumUnaligned()
{
var refValues = Enumerable.Range(-1, 3).Select(x => (long)x).ToArray();
var buffer = new RcCyclicBuffer<long>(refValues.Length, refValues);
Assert.That(RcCyclicBuffers.Sum(buffer), Is.EqualTo(refValues.Sum()));
}
[Test]
public void RcCyclicBuffers_AverageUnaligned()
{
var refValues = Enumerable.Range(-1, 3).Select(x => (long)x).ToArray();
var buffer = new RcCyclicBuffer<long>(refValues.Length, refValues);
Assert.That(RcCyclicBuffers.Average(buffer), Is.EqualTo(refValues.Average()));
}
[Test]
public void RcCyclicBuffers_MinUnaligned()
{
var refValues = Enumerable.Range(5, 3).Select(x => (long)x).ToArray();
var buffer = new RcCyclicBuffer<long>(refValues.Length, refValues);
Assert.That(RcCyclicBuffers.Min(buffer), Is.EqualTo(refValues.Min()));
}
[Test]
public void RcCyclicBuffers_MaxUnaligned()
{
var refValues = Enumerable.Range(-5, 3).Select(x => (long)x).ToArray();
var buffer = new RcCyclicBuffer<long>(refValues.Length, refValues);
Assert.That(RcCyclicBuffers.Max(buffer), Is.EqualTo(refValues.Max()));
}
}