Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a Remainder method #44

Merged
merged 13 commits into from Sep 10, 2019
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using BenchmarkDotNet.Attributes;
using OpenTK;
using System.Runtime.Intrinsics.X86;

namespace MathSharp.Interactive.Benchmarks.Vector.Double
{
using Vector = MathSharp.Vector;
public class RemainderBenchmark
{
private Vector4d vec;

[GlobalSetup]
public void Setup()
{
vec = new Vector4d(0, 1.2f, 1.6f, 3.2f);
}
[Benchmark]
public void Normal()
{
var result = normal(vec, 2);
}

[Benchmark]
public void MathSharp()
{
var result = mathSharp(vec, 2);
}

//Prevents the JIT from compiling Normal() away.
private unsafe Vector4d mathSharp(Vector4d vector, double divisor)
{
var v = Avx.LoadVector256((double*)&vector);
v = Vector.Remainder(v, divisor);

Vector4d result;
Avx.Store((double*)&result, v);
return result;
}

private Vector4d normal(Vector4d vector, double divisor)
=> new Vector4d(vector.X % divisor, vector.Y % divisor, vector.Z % divisor, vector.W % divisor);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using BenchmarkDotNet.Attributes;
using System.Numerics;
namespace MathSharp.Interactive.Benchmarks.Vector.Single
{
using Vector = MathSharp.Vector;
public class RemainderBenchmark
{
private Vector4 vec;

[GlobalSetup]
public void Setup()
{
vec = new Vector4(0, 1.2f, 1.6f, 3.2f);
}
[Benchmark]
public void Normal()
{
var result = normal(vec, 2);
}

[Benchmark]
public void MathSharp()
{
var result = mathSharp(vec, 2);
}

//Prevents the JIT from compiling Normal() away.
private Vector4 mathSharp(Vector4 vector, float divisor)
{
var v = vector.Load();
v = Vector.Remainder(v, divisor);
Vector.Store(v, out Vector4 result);
return result;
}

private Vector4 normal(Vector4 vector, float divisor)
=> new Vector4(vector.X % divisor, vector.Y % divisor, vector.Z % divisor, vector.W % divisor);
}
}
2 changes: 1 addition & 1 deletion samples/MathSharp.Interactive/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ internal class Program
{
private static void Main(string[] args)
{
BenchmarkRunner.Run<SinBenchmark>();
BenchmarkRunner.Run<RemainderBenchmark>();
}

private static readonly Vector3 Direction = new Vector3(1, 2, 3);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
using System.Runtime.CompilerServices;
using System;
using System.Runtime.CompilerServices;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
using MathSharp.Attributes;
using static MathSharp.Utils.Helpers;
using static MathSharp.SoftwareFallbacks;

namespace MathSharp
Expand All @@ -13,12 +15,12 @@ public static partial class Vector
{
#region Vector


[MethodImpl(MaxOpt)]
public static Vector4D Abs(in Vector4DParam1_3 vector)
public static Vector4D Abs(in Vector4DParam1_3 vector)
=> Max(Subtract(Vector4D.Zero, vector), vector);


[MethodImpl(MaxOpt)]
public static Vector4D HorizontalAdd(in Vector4DParam1_3 left, in Vector4DParam1_3 right)
{
Expand All @@ -30,7 +32,7 @@ public static Vector4D HorizontalAdd(in Vector4DParam1_3 left, in Vector4DParam1
return HorizontalAdd_Software(left, right);
}


[MethodImpl(MaxOpt)]
public static Vector4D Add(in Vector4DParam1_3 left, in Vector4DParam1_3 right)
{
Expand All @@ -42,12 +44,12 @@ public static Vector4D Add(in Vector4DParam1_3 left, in Vector4DParam1_3 right)
return Add_Software(left, right);
}


[MethodImpl(MaxOpt)]
public static Vector4D Add(in Vector4DParam1_3 vector, double scalar)
=> Add(vector, Vector256.Create(scalar));


[MethodImpl(MaxOpt)]
public static Vector4D Subtract(in Vector4DParam1_3 left, in Vector4DParam1_3 right)
{
Expand All @@ -59,12 +61,12 @@ public static Vector4D Subtract(in Vector4DParam1_3 left, in Vector4DParam1_3 ri
return Subtract_Software(left, right);
}


[MethodImpl(MaxOpt)]
public static Vector4D Subtract(in Vector4DParam1_3 vector, double scalar)
=> Subtract(vector, Vector256.Create(scalar));


[MethodImpl(MaxOpt)]
public static Vector4D Multiply(in Vector4DParam1_3 left, in Vector4DParam1_3 right)
{
Expand All @@ -91,16 +93,16 @@ public static Vector4D Divide(in Vector4DParam1_3 dividend, in Vector4DParam1_3
return Divide_Software(dividend, divisor);
}


[MethodImpl(MaxOpt)]
public static Vector4D Divide(in Vector4DParam1_3 dividend, double scalarDivisor)
=> Subtract(dividend, Vector256.Create(scalarDivisor));

public static Vector4D Clamp(in Vector4DParam1_3 vector, in Vector4DParam1_3 low, in Vector4DParam1_3 high)

public static Vector4D Clamp(in Vector4DParam1_3 vector, in Vector4DParam1_3 low, in Vector4DParam1_3 high)
=> Max(Min(vector, high), low);


[MethodImpl(MaxOpt)]
public static Vector4D Sqrt(in Vector4DParam1_3 vector)
{
Expand All @@ -112,7 +114,7 @@ public static Vector4D Sqrt(in Vector4DParam1_3 vector)
return Sqrt_Software(vector);
}


[MethodImpl(MaxOpt)]
public static Vector4D Max(in Vector4DParam1_3 left, in Vector4DParam1_3 right)
{
Expand All @@ -124,7 +126,7 @@ public static Vector4D Max(in Vector4DParam1_3 left, in Vector4DParam1_3 right)
return Max_Software(left, right);
}


[MethodImpl(MaxOpt)]
public static Vector4D Min(in Vector4DParam1_3 left, in Vector4DParam1_3 right)
{
Expand All @@ -145,21 +147,55 @@ public static HwVector3D Negate(HwVector3D vector)
public static HwVector4D Negate(HwVector4D vector)
=> Negate4D(vector);


[MethodImpl(MaxOpt)]
public static Vector4D Negate2D(in Vector4DParam1_3 vector)
public static Vector4D Negate2D(in Vector4DParam1_3 vector)
=> Xor(vector, SignFlip2DDouble);


[MethodImpl(MaxOpt)]
public static Vector4D Negate3D(in Vector4DParam1_3 vector)
=> Xor(vector, SignFlip3DDouble);


[MethodImpl(MaxOpt)]
public static Vector4D Negate4D(in Vector4DParam1_3 vector)
=> Xor(vector, SignFlip4DDouble);

[MethodImpl(MaxOpt)]
public static Vector4D Remainder(in Vector4DParam1_3 left, in Vector4DParam1_3 right)
{
var n = Divide(left, right);
n = Truncate(n);

var y = Multiply(n, right);

return Subtract(left, y);
}

public static Vector4D Remainder(in Vector4DParam1_3 left, double right)
=> Remainder(left, Vector256.Create(right));

[MethodImpl(MaxOpt)]
public static Vector4D Truncate(in Vector4DParam1_3 vector)
{
if (Avx.IsSupported)
{
return Avx.RoundToZero(vector);
}

return SoftwareFallback(vector);

static Vector4D SoftwareFallback(in Vector4DParam1_3 vector)
{
return Vector256.Create(
Math.Truncate(X(vector)),
Math.Truncate(Y(vector)),
Math.Truncate(Z(vector)),
Math.Truncate(W(vector))
);
}
}
#endregion
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,20 @@ public static Vector4F Sin(in Vector4FParam1_3 vector)
return result;
}

[MethodImpl(MaxOpt)]
public static Vector4F Remainder(in Vector4FParam1_3 left, in Vector4FParam1_3 right)
{
var n = Divide(left, right);
n = Truncate(n);

var y = Multiply(n, right);

return Subtract(left, y);
}

public static Vector4F Remainder(in Vector4FParam1_3 left, float right)
=> Remainder(left, Vector128.Create(right));

private static readonly Vector4F OneDiv2Pi = Vector128.Create(SingleConstants.OneDiv2Pi);
private static readonly Vector4F Pi2 = Vector128.Create(SingleConstants.Pi2);
private static readonly Vector4F Pi = Vector128.Create(SingleConstants.Pi);
Expand All @@ -285,7 +299,6 @@ public static Vector4F Mod2Pi(in Vector4FParam1_3 vector)

return Subtract(vector, result);
}

[MethodImpl(MaxOpt)]
public static Vector4F Round(in Vector4FParam1_3 vector)
{
Expand All @@ -309,6 +322,27 @@ static Vector4F SoftwareFallback(in Vector4FParam1_3 vector)
}
}

[MethodImpl(MaxOpt)]
public static Vector4F Truncate(in Vector4FParam1_3 vector)
{
if (Sse41.IsSupported)
{
return Sse41.RoundToZero(vector);
}

return SoftwareFallback(vector);

static Vector4F SoftwareFallback(in Vector4FParam1_3 vector)
{
return Vector128.Create(
MathF.Truncate(X(vector)),
MathF.Truncate(Y(vector)),
MathF.Truncate(Z(vector)),
MathF.Truncate(W(vector))
);
}
}

#endregion
}
}
16 changes: 16 additions & 0 deletions tests/MathSharp.UnitTests/Helpers/TestHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
using static MathSharp.Utils.Helpers;
using MathSharp.Utils;
using OpenTK;
using Vec2 = System.Numerics.Vector2;
using Vec3 = System.Numerics.Vector3;
using Vec4 = System.Numerics.Vector4;

namespace MathSharp.UnitTests
{
Expand Down Expand Up @@ -57,10 +60,23 @@ public static bool AreEqual(Vector3 left, Vector128<float> right)
public static bool AreEqual(Vector2 left, Vector128<float> right)
=> left.X.Equals(X(right)) && left.Y.Equals(Y(right));

[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static bool AreEqual(Vec4 left, Vector128<float> right)
=> left.X.Equals(X(right)) && left.Y.Equals(Y(right)) && left.Z.Equals(Z(right)) && left.W.Equals(W(right));

[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static bool AreEqual(Vec3 left, Vector128<float> right)
=> left.X.Equals(X(right)) && left.Y.Equals(Y(right)) && left.Z.Equals(Z(right));

[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static bool AreEqual(Vec2 left, Vector128<float> right)
=> left.X.Equals(X(right)) && left.Y.Equals(Y(right));

[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static bool AreEqual(float left, Vector128<float> right)
=> left.Equals(X(right));


[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static bool AreApproxEqual(float left, float right, float interval = 0.000000001f)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using OpenTK;
using System;
using System.Collections.Generic;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
using System.Text;
using Xunit;
using Xunit.Abstractions;

namespace MathSharp.UnitTests.VectorTests.VectorDouble.BasicMathsTests
{
public class Remainder
{
public static IEnumerable<object[]> Data =>
new[]
{
new object[] { new Vector4d(10.4f), new Vector4d(2f) },
new object[] { new Vector4d(-10.4f), new Vector4d(2f) },
new object[] { new Vector4d(float.NaN, float.MinValue, float.MaxValue, float.PositiveInfinity), new Vector4d(3f) },
new object[] { new Vector4d(float.NaN, float.MinValue, float.MaxValue, float.PositiveInfinity), new Vector4d(2.4f) },
new object[] { new Vector4d(23.45f), new Vector4d(219f) },
new object[] { new Vector4d(12), new Vector4d(float.NaN) },
new object[] { new Vector4d(-10000000f), new Vector4d(2.2f) },
new object[] { new Vector4d(10000000f), new Vector4d(2.2f) },
new object[] { new Vector4d(float.NegativeInfinity), new Vector4d(3f) }
};

[Theory]
[MemberData(nameof(Data))]
public unsafe void Remainder_Theory(Vector4d left, Vector4d right)
{
var l = Avx.LoadAlignedVector256((double*)&left);
var r = Avx.LoadAlignedVector256((double*)&right);

var remainder = Vector.Remainder(l, r);
var expected = new Vector4d(left.X % right.X, left.Y % right.Y, left.Z % right.Z, left.W % right.W);

TestHelpers.AreEqual(expected, remainder);
}
}
}