Skip to content

Commit

Permalink
+ Added FastScalarMultiply
Browse files Browse the repository at this point in the history
+ Added some unit tests
  • Loading branch information
MineCake147E committed Jul 6, 2019
1 parent 63014dd commit 3165784
Show file tree
Hide file tree
Showing 3 changed files with 146 additions and 21 deletions.
7 changes: 7 additions & 0 deletions MonoAudio.Core/MonoAudio.Core.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

53 changes: 44 additions & 9 deletions MonoAudio.Core/SystemExtensions/Memory/SpanExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,18 +93,53 @@ public static void FastAdd(ReadOnlySpan<float> samplesToAdd, Span<float> buffer)
unsafe
{
(int newLength, int remainder) = MathI.FloorStepRem(samplesToAdd.Length, Vector<float>.Count);
var src = MemoryMarshal.Cast<float, Vector<float>>(samplesToAdd);
var dst = MemoryMarshal.Cast<float, Vector<float>>(buffer);
for (int i = 0; i < src.Length; i++)
if (newLength != 0)
{
dst[i] += src[i];
var src = MemoryMarshal.Cast<float, Vector<float>>(samplesToAdd);
var dst = MemoryMarshal.Cast<float, Vector<float>>(buffer);
for (int i = 0; i < src.Length; i++)
{
dst[i] += src[i];
}
}
if (remainder == 0) return;
var srcRem = samplesToAdd.Slice(newLength);
var dstRem = buffer.Slice(newLength);
for (int i = 0; i < srcRem.Length; i++)
if (remainder != 0)
{
dstRem[i] += srcRem[i];
var srcRem = samplesToAdd.Slice(newLength);
var dstRem = buffer.Slice(newLength);
for (int i = 0; i < srcRem.Length; i++)
{
dstRem[i] += srcRem[i];
}
}
}
}

/// <summary>
/// Multiplies the specified samples faster, with the given <paramref name="scale"/>.
/// </summary>
/// <param name="span">The span to multiply.</param>
/// <param name="scale">The value to be multiplied.</param>
public static void FastScalarMultiply(this Span<float> span, float scale = default)
{
if (Vector<float>.Count > span.Length)
{
for (int i = 0; i < span.Length; i++)
{
span[i] *= scale;
}
}
else
{
var spanV = MemoryMarshal.Cast<float, Vector<float>>(span);
var scaleV = new Vector<float>(scale);
for (int i = 0; i < spanV.Length; i++)
{
spanV[i] *= scaleV;
}
var spanR = span.Slice(spanV.Length * Vector<float>.Count);
for (int i = 0; i < spanR.Length; i++)
{
spanR[i] *= scale;
}
}
}
Expand Down
107 changes: 95 additions & 12 deletions Tests/MonoAudio.Core.Tests.CoreFx/SpanExtensionsTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,32 @@ namespace MonoAudio.Core.Tests.CoreFx
[TestFixture]
public class SpanExtensionsTest
{
[TestCase(4783)] //Prime number
[Test]
public void FastFillFillsCorrectly()
{
Span<float> span = stackalloc float[32];
const int Value = 1;
span.FastFill(Value);
for (int i = 0; i < span.Length; i++)
{
if (span[i] != Value) Assert.Fail("The FastFill doesn't fill correctly!");
}
Assert.Pass();
}

[TestCase(2459)] //Prime number
[TestCase(2048)] //2^11
[TestCase(4096)] //2^12
[TestCase(8192)] //2^13
public void FastFillTest(int length)
public void FastFillFasterThanNormalFill(int length)
{
var sw = new Stopwatch();
Span<float> source = stackalloc float[length];
Span<float> span = stackalloc float[length];
long cntFast = 0;
long cntStandard = 0;
Thread.Sleep(50);
sw.Start();
do
{
source.FastFill(cntFast);
span.FastFill(cntFast);
cntFast++;
} while (sw.ElapsedMilliseconds < length);
sw.Stop();
Expand All @@ -35,19 +46,35 @@ public void FastFillTest(int length)
sw.Start();
do
{
source.Fill(cntStandard);
span.Fill(cntStandard);
cntStandard++;
} while (sw.ElapsedMilliseconds < length);
sw.Stop();
Console.WriteLine(cntStandard);
Console.WriteLine($"{nameof(SpanExtensions.FastFill)} seems to be {(double)cntFast / cntStandard} times faster than {nameof(Span<float>.Fill)}");
Console.WriteLine($"{nameof(SpanExtensions.FastFill)} seems to be {(double)cntFast / cntStandard} times faster than {nameof(Span<float>.Fill)}.");
Assert.Greater(cntFast, cntStandard);
}

[TestCase(4783)] //Prime number
[Test]
public void FastAddAddsCorrectly()
{
Span<float> source = stackalloc float[32];
Span<float> destination = stackalloc float[48];
const int Value = 1;
source.FastFill(Value);
destination.FastFill(-1);

SpanExtensions.FastAdd(source, destination);
for (int i = 0; i < source.Length; i++)
{
if (destination[i] != 0) Assert.Fail("The FastAdd doesn't add correctly!");
}
Assert.Pass();
}

[TestCase(2459)] //Prime number
[TestCase(2048)] //2^11
[TestCase(4096)] //2^12
public void FastAddTest(int length)
public void FastAddFasterThanUnsafeAdd(int length)
{
var sw = new Stopwatch();
Span<float> source = stackalloc float[length];
Expand Down Expand Up @@ -87,7 +114,63 @@ public void FastAddTest(int length)
} while (sw.ElapsedMilliseconds < length);
sw.Stop();
Console.WriteLine(cntStandard);
Console.WriteLine($"{nameof(SpanExtensions.FastFill)} seems to be {(double)cntFast / cntStandard} times faster than unsafe loop");
Console.WriteLine($"{nameof(SpanExtensions.FastAdd)} seems to be {(double)cntFast / cntStandard} times faster than unsafe loop.");
Assert.Greater(cntFast, cntStandard);
}

[Test]
public void FastScalarMultiplyScalesCorrectly()
{
Span<float> span = stackalloc float[32];
span.FastFill(1);
const float Value = MathF.PI;
span.FastScalarMultiply(Value);
for (int i = 0; i < span.Length; i++)
{
if (span[i] != Value) Assert.Fail("The FastScalarMultiply doesn't scale correctly!");
}
Assert.Pass();
}

[TestCase(2459)] //Prime number
[TestCase(2048)] //2^11
public void FastScalarMultiplyFasterThanUnsafe(int length)
{
var sw = new Stopwatch();
Span<float> span = stackalloc float[length];
span.FastFill(1);
long cntFast = 0;
long cntStandard = 0;
Thread.Sleep(50);
sw.Start();
do
{
span.FastScalarMultiply(cntFast);
cntFast++;
} while (sw.ElapsedMilliseconds < length);
sw.Stop();
Console.WriteLine(cntFast);
sw.Reset();
Thread.Sleep(50);
sw.Start();
do
{
unsafe
{
fixed (float* srcFx = span)
{
var src = srcFx;
do
{
*src *= cntStandard;
} while (++src < srcFx + span.Length);
}
}
cntStandard++;
} while (sw.ElapsedMilliseconds < length);
sw.Stop();
Console.WriteLine(cntStandard);
Console.WriteLine($"{nameof(SpanExtensions.FastScalarMultiply)} seems to be {(double)cntFast / cntStandard} times faster than unsafe loop.");
Assert.Greater(cntFast, cntStandard);
}
}
Expand Down

0 comments on commit 3165784

Please sign in to comment.