diff --git a/Misaki.HighPerformance.Mathematics.CodeGen/Generators/VectorGenerator.cs b/Misaki.HighPerformance.Mathematics.CodeGen/Generators/VectorGenerator.cs index 0325abd..a1168c8 100644 --- a/Misaki.HighPerformance.Mathematics.CodeGen/Generators/VectorGenerator.cs +++ b/Misaki.HighPerformance.Mathematics.CodeGen/Generators/VectorGenerator.cs @@ -128,7 +128,7 @@ namespace Misaki.HighPerformance.Mathematics.CodeGen.Generators get {{ RangeCheck(index); - return ref (({typeInfo.ComponentTypeFullName}*)global::System.Runtime.CompilerServices.Unsafe.AsPointer(ref this))[index]; + return ref global::System.Runtime.CompilerServices.Unsafe.Add(ref {s_vectorComponents[0]}, index); }} }}"); diff --git a/Misaki.HighPerformance.Mathematics.SPMD/IJobSPMD.cs b/Misaki.HighPerformance.Mathematics.SPMD/IJobSPMD.cs index 411be67..222d8ab 100644 --- a/Misaki.HighPerformance.Mathematics.SPMD/IJobSPMD.cs +++ b/Misaki.HighPerformance.Mathematics.SPMD/IJobSPMD.cs @@ -38,6 +38,20 @@ internal struct SPMDJobWrapper : IJobParallelFor } } +internal struct SPMDScalerJobWrapper : IJobParallelFor + where T : unmanaged, IJobSPMD + where TNumber : unmanaged, INumber, IMinMaxValue, IBitwiseOperators +{ + public T innerJob; + public int totalCount; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Execute(int loopIndex, int threadIndex) + { + innerJob.Execute>(loopIndex, threadIndex); + } +} + public static class IJobParallelForSPMDExtensions { public static void Run(this ref T job, int totalCount, int threadIndex) @@ -68,13 +82,26 @@ public static class IJobParallelForSPMDExtensions where T : unmanaged, IJobSPMD where TNumber : unmanaged, INumber, IMinMaxValue, IBitwiseOperators { - var warper = new SPMDJobWrapper + if (WideLane.IsSupported) { - innerJob = job, - totalCount = totalCount, - }; + var warper = new SPMDJobWrapper + { + innerJob = job, + totalCount = totalCount, + }; - var iterations = (totalCount + WideLane.LaneWidth - 1) / WideLane.LaneWidth; - return jobScheduler.ScheduleParallel(ref warper, iterations, batchSize, threadIndex, dependency); + var iterations = (totalCount + WideLane.LaneWidth - 1) / WideLane.LaneWidth; + return jobScheduler.ScheduleParallel(ref warper, iterations, batchSize, threadIndex, dependency); + } + else + { + var warper = new SPMDScalerJobWrapper + { + innerJob = job, + totalCount = totalCount, + }; + + return jobScheduler.ScheduleParallel(ref warper, totalCount, batchSize, threadIndex, dependency); + } } } diff --git a/Misaki.HighPerformance.Mathematics.SPMD/ISPMD.cs b/Misaki.HighPerformance.Mathematics.SPMD/ISPMD.cs index 9742ed2..0b7a643 100644 --- a/Misaki.HighPerformance.Mathematics.SPMD/ISPMD.cs +++ b/Misaki.HighPerformance.Mathematics.SPMD/ISPMD.cs @@ -1,6 +1,4 @@ using System.Numerics; -using System.Runtime.CompilerServices; -using static System.Runtime.InteropServices.JavaScript.JSType; namespace Misaki.HighPerformance.Mathematics.SPMD; @@ -18,12 +16,20 @@ public interface ISPMD } } +// TODO: +// - ReduceAdd +// - ReduceMin +// - ReduceMax +// - LeadingZeroCount +// - TrailingZeroCount +// - PopCount + /// /// Represents a single-lane or multi-lane (vectorized) SPMD value and the operations supported on it. /// /// The concrete SPMD lane type implementing this interface. /// The underlying numeric element type. -public interface ISPMD : ISPMD +public interface ISPMD : ISPMD, IEquatable where TSelf : ISPMD where TNumber : unmanaged, INumber, IMinMaxValue, IBitwiseOperators { @@ -159,13 +165,6 @@ public interface ISPMD : ISPMD /// The lane-wise sum. static abstract TSelf operator +(TSelf a, TSelf b); /// - /// Adds a lane value and a scalar element-wise. - /// - /// The lane value. - /// The scalar value. - /// The lane value with the scalar added to each element. - static abstract TSelf operator +(TSelf a, TNumber b); - /// /// Subtracts two lane values element-wise. /// /// The first lane value. @@ -173,13 +172,6 @@ public interface ISPMD : ISPMD /// The lane-wise difference. static abstract TSelf operator -(TSelf a, TSelf b); /// - /// Subtracts a scalar from a lane value element-wise. - /// - /// The lane value. - /// The scalar value. - /// The lane value with the scalar subtracted from each element. - static abstract TSelf operator -(TSelf a, TNumber b); - /// /// Multiplies two lane values element-wise. /// /// The first lane value. @@ -187,13 +179,6 @@ public interface ISPMD : ISPMD /// The lane-wise product. static abstract TSelf operator *(TSelf a, TSelf b); /// - /// Multiplies a lane value by a scalar element-wise. - /// - /// The lane value. - /// The scalar value. - /// The lane value scaled by the scalar. - static abstract TSelf operator *(TSelf a, TNumber b); - /// /// Divides two lane values element-wise. /// /// The first lane value. @@ -201,26 +186,12 @@ public interface ISPMD : ISPMD /// The lane-wise quotient. static abstract TSelf operator /(TSelf a, TSelf b); /// - /// Divides a lane value by a scalar element-wise. - /// - /// The lane value. - /// The scalar value. - /// The lane value divided by the scalar. - static abstract TSelf operator /(TSelf a, TNumber b); - /// /// Computes the modulus of two lane values element-wise. /// /// The first lane value. /// The second lane value. /// The lane-wise modulus. static abstract TSelf operator %(TSelf a, TSelf b); - /// - /// Computes the modulus of a lane value and a scalar element-wise. - /// - /// The lane value. - /// The scalar value. - /// The lane value modulus scalar. - static abstract TSelf operator %(TSelf a, TNumber b); /// /// Negates the lane value element-wise. @@ -237,13 +208,6 @@ public interface ISPMD : ISPMD /// The result of the bitwise AND. static abstract TSelf operator &(TSelf a, TSelf b); /// - /// Computes the bitwise AND of a lane value and a scalar element-wise. - /// - /// The lane value. - /// The scalar value. - /// The result of the bitwise AND. - static abstract TSelf operator &(TSelf a, TNumber b); - /// /// Computes the bitwise OR of two lane values element-wise. /// /// The first lane value. @@ -251,13 +215,6 @@ public interface ISPMD : ISPMD /// The result of the bitwise OR. static abstract TSelf operator |(TSelf a, TSelf b); /// - /// Computes the bitwise OR of a lane value and a scalar element-wise. - /// - /// The lane value. - /// The scalar value. - /// The result of the bitwise OR. - static abstract TSelf operator |(TSelf a, TNumber b); - /// /// Computes the bitwise XOR of two lane values element-wise. /// /// The first lane value. @@ -265,18 +222,27 @@ public interface ISPMD : ISPMD /// The result of the bitwise XOR. static abstract TSelf operator ^(TSelf a, TSelf b); /// - /// Computes the bitwise XOR of a lane value and a scalar element-wise. - /// - /// The lane value. - /// The scalar value. - /// The result of the bitwise XOR. - static abstract TSelf operator ^(TSelf a, TNumber b); - /// /// Computes the bitwise NOT of a lane value element-wise. /// /// The lane value. /// The bitwise complement of the lane value. static abstract TSelf operator ~(TSelf a); + /// + /// Determines whether two instances of the type are equal component-wise. + /// + /// The first value to compare. + /// The second value to compare. + /// All bits set where the elements are equal; otherwise, all bits cleared. + static abstract TSelf operator ==(TSelf a, TSelf b); + /// + /// Determines whether two instances of the type are not equal component-wise. + /// + /// The first value to compare. + /// The second value to compare. + /// All bits set where the elements are not equal; otherwise, all bits cleared. + static abstract TSelf operator !=(TSelf a, TSelf b); + + static abstract implicit operator TSelf(TNumber value); /// /// Computes the absolute value of the lane value element-wise. diff --git a/Misaki.HighPerformance.Mathematics.SPMD/MathV.cs b/Misaki.HighPerformance.Mathematics.SPMD/MathV.cs new file mode 100644 index 0000000..ee40357 --- /dev/null +++ b/Misaki.HighPerformance.Mathematics.SPMD/MathV.cs @@ -0,0 +1,153 @@ +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace Misaki.HighPerformance.Mathematics.SPMD; + +public static unsafe partial class MathV +{ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 LoadVector3(TNumber* pSrc) + where TLane : ISPMD + where TNumber : unmanaged, INumber, IMinMaxValue, IBitwiseOperators + { + var width = TLane.LaneWidth; + + var x = stackalloc TNumber[width]; + var y = stackalloc TNumber[width]; + var z = stackalloc TNumber[width]; + + for (var i = 0; i < width; i++) + { + x[i] = pSrc[i * 3 + 0]; + y[i] = pSrc[i * 3 + 1]; + z[i] = pSrc[i * 3 + 2]; + } + + return new Vector3 + { + x = TLane.Load(x), + y = TLane.Load(y), + z = TLane.Load(z) + }; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 Load(ref TNumber src) + where TLane : ISPMD + where TNumber : unmanaged, INumber, IMinMaxValue, IBitwiseOperators + { + return LoadVector3((TNumber*)Unsafe.AsPointer(ref src)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 Load(TNumber* pX, TNumber* pY, TNumber* pZ) + where TLane : ISPMD + where TNumber : unmanaged, INumber, IMinMaxValue, IBitwiseOperators + { + return new Vector3 + { + x = TLane.Load(pX), + y = TLane.Load(pY), + z = TLane.Load(pZ) + }; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 Load(ref TNumber x, ref TNumber y, ref TNumber z) + where TLane : ISPMD + where TNumber : unmanaged, INumber, IMinMaxValue, IBitwiseOperators + { + return new Vector3 + { + x = TLane.Load(ref x), + y = TLane.Load(ref y), + z = TLane.Load(ref z) + }; + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 Abs(in Vector3 vector) + where TLane : ISPMD + where TNumber : unmanaged, INumber, IMinMaxValue, IBitwiseOperators + { + return new Vector3 + { + x = TLane.Abs(vector.x), + y = TLane.Abs(vector.y), + z = TLane.Abs(vector.z), + }; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TLane Dot(in Vector3 a, in Vector3 b) + where TLane : ISPMD + where TNumber : unmanaged, INumber, IMinMaxValue, IBitwiseOperators + { + return a.x * b.x + a.y * b.y + a.z * b.z; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TLane LengthSquared(in Vector3 vector) + where TLane : ISPMD + where TNumber : unmanaged, INumber, IMinMaxValue, IBitwiseOperators + { + return Dot(vector, vector); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 Sqrt(in Vector3 vector) + where TLane : ISPMD + where TNumber : unmanaged, INumber, IMinMaxValue, IBitwiseOperators + { + return new Vector3 + { + x = TLane.Sqrt(vector.x), + y = TLane.Sqrt(vector.y), + z = TLane.Sqrt(vector.z), + }; + } + + public static Vector3 Rsqrt(in Vector3 vector) + where TLane : ISPMD + where TNumber : unmanaged, INumber, IMinMaxValue, IBitwiseOperators + { + return new Vector3 + { + x = TLane.Rsqrt(vector.x), + y = TLane.Rsqrt(vector.y), + z = TLane.Rsqrt(vector.z), + }; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 Normalize(in Vector3 vector) + where TLane : ISPMD + where TNumber : unmanaged, INumber, IMinMaxValue, IBitwiseOperators + { + return vector * TLane.Rsqrt(Dot(vector, vector)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 Cross(in Vector3 a, in Vector3 b) + where TLane : ISPMD + where TNumber : unmanaged, INumber, IMinMaxValue, IBitwiseOperators + { + return new Vector3 + { + x = a.y * b.z - a.z * b.y, + y = a.z * b.x - a.x * b.z, + z = a.x * b.y - a.y * b.x, + }; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 Reflect(in Vector3 incident, in Vector3 normal) + where TLane : ISPMD + where TNumber : unmanaged, INumber, IMinMaxValue, IBitwiseOperators + { + var dot = Dot(incident, normal); + return incident - normal * (dot + dot); + } +} diff --git a/Misaki.HighPerformance.Mathematics.SPMD/ScalerLane.cs b/Misaki.HighPerformance.Mathematics.SPMD/ScalerLane.cs index 9e47cf4..7ee411e 100644 --- a/Misaki.HighPerformance.Mathematics.SPMD/ScalerLane.cs +++ b/Misaki.HighPerformance.Mathematics.SPMD/ScalerLane.cs @@ -5,10 +5,10 @@ using System.Runtime.InteropServices; namespace Misaki.HighPerformance.Mathematics.SPMD; [StructLayout(LayoutKind.Sequential)] -public readonly unsafe struct ScalarLane : ISPMD, T> - where T : unmanaged, INumber, IMinMaxValue, IBitwiseOperators +public readonly unsafe struct ScalarLane : ISPMD, TNumber> + where TNumber : unmanaged, INumber, IMinMaxValue, IBitwiseOperators { - public readonly T value; + public readonly TNumber value; public static int LaneWidth { @@ -16,77 +16,99 @@ public readonly unsafe struct ScalarLane : ISPMD, T> get => 1; } - public static ScalarLane Zero + public static ScalarLane Zero { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => new(T.Zero); + get => new(TNumber.Zero); } - public static ScalarLane One + public static ScalarLane One { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => new(T.One); + get => new(TNumber.One); } - public static ScalarLane MinValue + public static ScalarLane MinValue { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => new(T.MinValue); + get => new(TNumber.MinValue); } - public static ScalarLane MaxValue + public static ScalarLane MaxValue { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => new(T.MaxValue); + get => new(TNumber.MaxValue); } - public readonly T this[int index] + public readonly TNumber this[int index] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => value; } - public ScalarLane(T value) + public ScalarLane(TNumber value) { this.value = value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ScalarLane Create(T value) => new(value); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ScalarLane Create(params ReadOnlySpan values) => new(values[0]); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ScalarLane Create(Vector value) => new(value[0]); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ScalarLane Sequence(T start, T step) => new(start); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ScalarLane Load(ref T value) => new(value); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ScalarLane Load(T* pValue) => new(*pValue); - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly void Store(ref T destination) => destination = value; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly void Store(T* pDestination) => *pDestination = value; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int CompressStore(ScalarLane mask, ref T destination) + public static ScalarLane Create(TNumber value) { - return CompressStore(mask, (T*)Unsafe.AsPointer(in destination)); + return new(value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int CompressStore(ScalarLane mask, T* pDestination) + public static ScalarLane Create(params ReadOnlySpan values) { - if (mask.value != T.Zero) + return new(values[0]); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ScalarLane Create(Vector value) + { + return new(value[0]); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ScalarLane Sequence(TNumber start, TNumber step) + { + return new(start); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ScalarLane Load(ref TNumber value) + { + return new(value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ScalarLane Load(TNumber* pValue) + { + return new(*pValue); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly void Store(ref TNumber destination) + { + destination = value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly void Store(TNumber* pDestination) + { + *pDestination = value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int CompressStore(ScalarLane mask, ref TNumber destination) + { + return CompressStore(mask, (TNumber*)Unsafe.AsPointer(in destination)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int CompressStore(ScalarLane mask, TNumber* pDestination) + { + if (mask.value != TNumber.Zero) { *pDestination = value; return 1; @@ -96,299 +118,358 @@ public readonly unsafe struct ScalarLane : ISPMD, T> } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly Vector AsVector() => Vector.Create(value); - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ScalarLane operator +(ScalarLane a, ScalarLane b) => new(a.value + b.value); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ScalarLane operator +(ScalarLane a, T b) => new(a.value + b); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ScalarLane operator -(ScalarLane a, ScalarLane b) => new(a.value - b.value); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ScalarLane operator -(ScalarLane a, T b) => new(a.value - b); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ScalarLane operator *(ScalarLane a, ScalarLane b) => new(a.value * b.value); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ScalarLane operator *(ScalarLane a, T b) => new(a.value * b); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ScalarLane operator /(ScalarLane a, ScalarLane b) => new(a.value / b.value); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ScalarLane operator /(ScalarLane a, T b) => new(a.value / b); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ScalarLane operator %(ScalarLane a, ScalarLane b) => new(a.value % b.value); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ScalarLane operator %(ScalarLane a, T b) => new(a.value % b); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ScalarLane operator -(ScalarLane a) => new(-a.value); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ScalarLane operator &(ScalarLane a, ScalarLane b) => new(a.value & b.value); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ScalarLane operator &(ScalarLane a, T b) => new(a.value & b); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ScalarLane operator |(ScalarLane a, ScalarLane b) => new(a.value | b.value); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ScalarLane operator |(ScalarLane a, T b) => new(a.value | b); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ScalarLane operator ^(ScalarLane a, ScalarLane b) => new(a.value ^ b.value); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ScalarLane operator ^(ScalarLane a, T b) => new(a.value ^ b); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ScalarLane operator ~(ScalarLane a) => new(~a.value); - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ScalarLane Abs(ScalarLane value) => new(T.Abs(value.value)); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ScalarLane Floor(ScalarLane value) + public readonly Vector AsVector() { - // Note: INumber does not provide Floor method, so we need to handle float and double specifically. - // This is acceptable for performance because JIT generates specialized code for each T as long as they are struct. - // Which mean for ScalarLane, typeof(T) == typeof(float) is always true and jit will optimize away the other branches. - if (typeof(T) == typeof(float)) + return Vector.Create(value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ScalarLane operator +(ScalarLane a, ScalarLane b) + { + return new(a.value + b.value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ScalarLane operator -(ScalarLane a, ScalarLane b) + { + return new(a.value - b.value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ScalarLane operator *(ScalarLane a, ScalarLane b) + { + return new(a.value * b.value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ScalarLane operator /(ScalarLane a, ScalarLane b) + { + return new(a.value / b.value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ScalarLane operator %(ScalarLane a, ScalarLane b) + { + return new(a.value % b.value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ScalarLane operator -(ScalarLane a) + { + return new(-a.value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ScalarLane operator &(ScalarLane a, ScalarLane b) + { + return new(a.value & b.value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ScalarLane operator |(ScalarLane a, ScalarLane b) + { + return new(a.value | b.value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ScalarLane operator ^(ScalarLane a, ScalarLane b) + { + return new(a.value ^ b.value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ScalarLane operator ~(ScalarLane a) + { + return new(~a.value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ScalarLane operator ==(ScalarLane a, ScalarLane b) + { + return new(a.value == b.value ? ~TNumber.Zero : TNumber.Zero); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ScalarLane operator !=(ScalarLane a, ScalarLane b) + { + return new(a.value != b.value ? ~TNumber.Zero : TNumber.Zero); + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator ScalarLane(TNumber value) + { + return new(value); + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ScalarLane Abs(ScalarLane value) + { + return new(TNumber.Abs(value.value)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ScalarLane Floor(ScalarLane value) + { + // Note: INumber does not provide Floor method, so we need to handle float and double specifically. + // This is acceptable for performance because JIT generates specialized code for each TLane as long as they are struct. + // Which mean for ScalarLane, typeof(TLane) == typeof(float) is always true and jit will optimize away the other branches. + if (typeof(TNumber) == typeof(float)) { - var f = Unsafe.As, float>(ref value); + var f = Unsafe.As, float>(ref value); var result = MathF.Floor(f); - return Unsafe.As>(ref result); + return Unsafe.As>(ref result); } - else if (typeof(T) == typeof(double)) + else if (typeof(TNumber) == typeof(double)) { - var d = Unsafe.As, double>(ref value); + var d = Unsafe.As, double>(ref value); var result = Math.Floor(d); - return Unsafe.As>(ref result); + return Unsafe.As>(ref result); } return value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ScalarLane Frac(ScalarLane value) => new(value.value - T.CreateTruncating(value.value)); + public static ScalarLane Frac(ScalarLane value) + { + return new(value.value - TNumber.CreateTruncating(value.value)); + } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ScalarLane Sqrt(ScalarLane value) + public static ScalarLane Sqrt(ScalarLane value) { - if (typeof(T) == typeof(float)) + if (typeof(TNumber) == typeof(float)) { - var f = Unsafe.As, float>(ref value); + var f = Unsafe.As, float>(ref value); var result = MathF.Sqrt(f); - return Unsafe.As>(ref result); + return Unsafe.As>(ref result); } - else if (typeof(T) == typeof(double)) + else if (typeof(TNumber) == typeof(double)) { - var d = Unsafe.As, double>(ref value); + var d = Unsafe.As, double>(ref value); var result = Math.Sqrt(d); - return Unsafe.As>(ref result); + return Unsafe.As>(ref result); } return value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ScalarLane Lerp(ScalarLane a, ScalarLane b, ScalarLane t) => new(a.value + (b.value - a.value) * t.value); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ScalarLane MultipleAdd(ScalarLane a, ScalarLane b, ScalarLane c) => new(T.MultiplyAddEstimate(a.value, b.value, c.value)); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ScalarLane Min(ScalarLane a, ScalarLane b) => new(T.Min(a.value, b.value)); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ScalarLane Max(ScalarLane a, ScalarLane b) => new(T.Max(a.value, b.value)); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ScalarLane Clamp(ScalarLane value, ScalarLane min, ScalarLane max) => new(T.Clamp(value.value, min.value, max.value)); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ScalarLane Saturate(ScalarLane value) => Clamp(value, new(T.Zero), new(T.One)); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ScalarLane Sin(ScalarLane value) + public static ScalarLane Lerp(ScalarLane a, ScalarLane b, ScalarLane t) { - if (typeof(T) == typeof(float)) + return new(a.value + (b.value - a.value) * t.value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ScalarLane MultipleAdd(ScalarLane a, ScalarLane b, ScalarLane c) + { + return new(TNumber.MultiplyAddEstimate(a.value, b.value, c.value)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ScalarLane Min(ScalarLane a, ScalarLane b) + { + return new(TNumber.Min(a.value, b.value)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ScalarLane Max(ScalarLane a, ScalarLane b) + { + return new(TNumber.Max(a.value, b.value)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ScalarLane Clamp(ScalarLane value, ScalarLane min, ScalarLane max) + { + return new(TNumber.Clamp(value.value, min.value, max.value)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ScalarLane Saturate(ScalarLane value) + { + return Clamp(value, new(TNumber.Zero), new(TNumber.One)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ScalarLane Sin(ScalarLane value) + { + if (typeof(TNumber) == typeof(float)) { - var f = Unsafe.As, float>(ref value); + var f = Unsafe.As, float>(ref value); var result = MathF.Sin(f); - return Unsafe.As>(ref result); + return Unsafe.As>(ref result); } - else if (typeof(T) == typeof(double)) + else if (typeof(TNumber) == typeof(double)) { - var d = Unsafe.As, double>(ref value); + var d = Unsafe.As, double>(ref value); var result = Math.Sin(d); - return Unsafe.As>(ref result); + return Unsafe.As>(ref result); } return value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ScalarLane Cos(ScalarLane value) + public static ScalarLane Cos(ScalarLane value) { - if (typeof(T) == typeof(float)) + if (typeof(TNumber) == typeof(float)) { - var f = Unsafe.As, float>(ref value); + var f = Unsafe.As, float>(ref value); var result = MathF.Cos(f); - return Unsafe.As>(ref result); + return Unsafe.As>(ref result); } - else if (typeof(T) == typeof(double)) + else if (typeof(TNumber) == typeof(double)) { - var d = Unsafe.As, double>(ref value); + var d = Unsafe.As, double>(ref value); var result = Math.Cos(d); - return Unsafe.As>(ref result); + return Unsafe.As>(ref result); } return value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static (ScalarLane sin, ScalarLane cos) SinCos(ScalarLane value) => (Sin(value), Cos(value)); + public static (ScalarLane sin, ScalarLane cos) SinCos(ScalarLane value) + { + return (Sin(value), Cos(value)); + } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ScalarLane Tan(ScalarLane value) + public static ScalarLane Tan(ScalarLane value) { - if (typeof(T) == typeof(float)) + if (typeof(TNumber) == typeof(float)) { - var f = Unsafe.As, float>(ref value); + var f = Unsafe.As, float>(ref value); var result = MathF.Tan(f); - return Unsafe.As>(ref result); + return Unsafe.As>(ref result); } - else if (typeof(T) == typeof(double)) + else if (typeof(TNumber) == typeof(double)) { - var d = Unsafe.As, double>(ref value); + var d = Unsafe.As, double>(ref value); var result = Math.Tan(d); - return Unsafe.As>(ref result); + return Unsafe.As>(ref result); } return value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ScalarLane Asin(ScalarLane value) + public static ScalarLane Asin(ScalarLane value) { - if (typeof(T) == typeof(float)) + if (typeof(TNumber) == typeof(float)) { - var f = Unsafe.As, float>(ref value); + var f = Unsafe.As, float>(ref value); var result = MathF.Asin(f); - return Unsafe.As>(ref result); + return Unsafe.As>(ref result); } - else if (typeof(T) == typeof(double)) + else if (typeof(TNumber) == typeof(double)) { - var d = Unsafe.As, double>(ref value); + var d = Unsafe.As, double>(ref value); var result = Math.Asin(d); - return Unsafe.As>(ref result); + return Unsafe.As>(ref result); } return value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ScalarLane Acos(ScalarLane value) + public static ScalarLane Acos(ScalarLane value) { - if (typeof(T) == typeof(float)) + if (typeof(TNumber) == typeof(float)) { - var f = Unsafe.As, float>(ref value); + var f = Unsafe.As, float>(ref value); var result = MathF.Acos(f); - return Unsafe.As>(ref result); + return Unsafe.As>(ref result); } - else if (typeof(T) == typeof(double)) + else if (typeof(TNumber) == typeof(double)) { - var d = Unsafe.As, double>(ref value); + var d = Unsafe.As, double>(ref value); var result = Math.Acos(d); - return Unsafe.As>(ref result); + return Unsafe.As>(ref result); } return value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ScalarLane Atan(ScalarLane value) + public static ScalarLane Atan(ScalarLane value) { - if (typeof(T) == typeof(float)) + if (typeof(TNumber) == typeof(float)) { - var f = Unsafe.As, float>(ref value); + var f = Unsafe.As, float>(ref value); var result = MathF.Atan(f); - return Unsafe.As>(ref result); + return Unsafe.As>(ref result); } - else if (typeof(T) == typeof(double)) + else if (typeof(TNumber) == typeof(double)) { - var d = Unsafe.As, double>(ref value); + var d = Unsafe.As, double>(ref value); var result = Math.Atan(d); - return Unsafe.As>(ref result); + return Unsafe.As>(ref result); } return value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ScalarLane Atan2(ScalarLane y, ScalarLane x) + public static ScalarLane Atan2(ScalarLane y, ScalarLane x) { - if (typeof(T) == typeof(float)) + if (typeof(TNumber) == typeof(float)) { - var fy = Unsafe.As, float>(ref y); - var fx = Unsafe.As, float>(ref x); + var fy = Unsafe.As, float>(ref y); + var fx = Unsafe.As, float>(ref x); var result = MathF.Atan2(fy, fx); - return Unsafe.As>(ref result); + return Unsafe.As>(ref result); } - else if (typeof(T) == typeof(double)) + else if (typeof(TNumber) == typeof(double)) { - var dy = Unsafe.As, double>(ref y); - var dx = Unsafe.As, double>(ref x); + var dy = Unsafe.As, double>(ref y); + var dx = Unsafe.As, double>(ref x); var result = Math.Atan2(dy, dx); - return Unsafe.As>(ref result); + return Unsafe.As>(ref result); } return y; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ScalarLane Pow(ScalarLane x, ScalarLane y) + public static ScalarLane Pow(ScalarLane x, ScalarLane y) { - if (typeof(T) == typeof(float)) + if (typeof(TNumber) == typeof(float)) { - var fx = Unsafe.As, float>(ref x); - var fy = Unsafe.As, float>(ref y); + var fx = Unsafe.As, float>(ref x); + var fy = Unsafe.As, float>(ref y); var result = MathF.Pow(fx, fy); - return Unsafe.As>(ref result); + return Unsafe.As>(ref result); } - else if (typeof(T) == typeof(double)) + else if (typeof(TNumber) == typeof(double)) { - var dx = Unsafe.As, double>(ref x); - var dy = Unsafe.As, double>(ref y); + var dx = Unsafe.As, double>(ref x); + var dy = Unsafe.As, double>(ref y); var result = Math.Pow(dx, dy); - return Unsafe.As>(ref result); + return Unsafe.As>(ref result); } return x; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ScalarLane Exp(ScalarLane value) + public static ScalarLane Exp(ScalarLane value) { - if (typeof(T) == typeof(float)) + if (typeof(TNumber) == typeof(float)) { - var f = Unsafe.As, float>(ref value); + var f = Unsafe.As, float>(ref value); var result = MathF.Exp(f); - return Unsafe.As>(ref result); + return Unsafe.As>(ref result); } - else if (typeof(T) == typeof(double)) + else if (typeof(TNumber) == typeof(double)) { - var d = Unsafe.As, double>(ref value); + var d = Unsafe.As, double>(ref value); var result = Math.Log(d); - return Unsafe.As>(ref result); + return Unsafe.As>(ref result); } return value; @@ -396,167 +477,222 @@ public readonly unsafe struct ScalarLane : ISPMD, T> [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ScalarLane Exp2(ScalarLane value) + public static ScalarLane Exp2(ScalarLane value) { - return Pow(new ScalarLane(T.CreateChecked(2)), value); + return Pow(new ScalarLane(TNumber.CreateChecked(2)), value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ScalarLane Log(ScalarLane value) + public static ScalarLane Log(ScalarLane value) { - if (typeof(T) == typeof(float)) + if (typeof(TNumber) == typeof(float)) { - var f = Unsafe.As, float>(ref value); + var f = Unsafe.As, float>(ref value); var result = MathF.Log(f); - return Unsafe.As>(ref result); + return Unsafe.As>(ref result); } - else if (typeof(T) == typeof(double)) + else if (typeof(TNumber) == typeof(double)) { - var d = Unsafe.As, double>(ref value); + var d = Unsafe.As, double>(ref value); var result = Math.Log(d); - return Unsafe.As>(ref result); + return Unsafe.As>(ref result); } return value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ScalarLane Log2(ScalarLane value) + public static ScalarLane Log2(ScalarLane value) { - if (typeof(T) == typeof(float)) + if (typeof(TNumber) == typeof(float)) { - var f = Unsafe.As, float>(ref value); + var f = Unsafe.As, float>(ref value); var result = MathF.Log2(f); - return Unsafe.As>(ref result); + return Unsafe.As>(ref result); } - else if (typeof(T) == typeof(double)) + else if (typeof(TNumber) == typeof(double)) { - var d = Unsafe.As, double>(ref value); + var d = Unsafe.As, double>(ref value); var result = Math.Log2(d); - return Unsafe.As>(ref result); + return Unsafe.As>(ref result); } return value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ScalarLane Ceil(ScalarLane value) + public static ScalarLane Ceil(ScalarLane value) { - if (typeof(T) == typeof(float)) + if (typeof(TNumber) == typeof(float)) { - var f = Unsafe.As, float>(ref value); + var f = Unsafe.As, float>(ref value); var result = MathF.Ceiling(f); - return Unsafe.As>(ref result); + return Unsafe.As>(ref result); } - else if (typeof(T) == typeof(double)) + else if (typeof(TNumber) == typeof(double)) { - var d = Unsafe.As, double>(ref value); + var d = Unsafe.As, double>(ref value); var result = Math.Ceiling(d); - return Unsafe.As>(ref result); + return Unsafe.As>(ref result); } - else if (typeof(T) == typeof(decimal)) + else if (typeof(TNumber) == typeof(decimal)) { - var d = Unsafe.As, decimal>(ref value); + var d = Unsafe.As, decimal>(ref value); var result = Math.Ceiling(d); - return Unsafe.As>(ref result); + return Unsafe.As>(ref result); } return value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ScalarLane Round(ScalarLane value) + public static ScalarLane Round(ScalarLane value) { - if (typeof(T) == typeof(float)) + if (typeof(TNumber) == typeof(float)) { - var f = Unsafe.As, float>(ref value); + var f = Unsafe.As, float>(ref value); var result = MathF.Round(f); - return Unsafe.As>(ref result); + return Unsafe.As>(ref result); } - else if (typeof(T) == typeof(double)) + else if (typeof(TNumber) == typeof(double)) { - var d = Unsafe.As, double>(ref value); + var d = Unsafe.As, double>(ref value); var result = Math.Round(d); - return Unsafe.As>(ref result); + return Unsafe.As>(ref result); } - else if (typeof(T) == typeof(decimal)) + else if (typeof(TNumber) == typeof(decimal)) { - var d = Unsafe.As, decimal>(ref value); + var d = Unsafe.As, decimal>(ref value); var result = Math.Round(d); - return Unsafe.As>(ref result); + return Unsafe.As>(ref result); } return value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ScalarLane Trunc(ScalarLane value) + public static ScalarLane Trunc(ScalarLane value) { - if (typeof(T) == typeof(float)) + if (typeof(TNumber) == typeof(float)) { - var f = Unsafe.As, float>(ref value); + var f = Unsafe.As, float>(ref value); var result = MathF.Truncate(f); - return Unsafe.As>(ref result); + return Unsafe.As>(ref result); } - else if (typeof(T) == typeof(double)) + else if (typeof(TNumber) == typeof(double)) { - var d = Unsafe.As, double>(ref value); + var d = Unsafe.As, double>(ref value); var result = Math.Truncate(d); - return Unsafe.As>(ref result); + return Unsafe.As>(ref result); } - else if (typeof(T) == typeof(decimal)) + else if (typeof(TNumber) == typeof(decimal)) { - var d = Unsafe.As, decimal>(ref value); + var d = Unsafe.As, decimal>(ref value); var result = Math.Truncate(d); - return Unsafe.As>(ref result); + return Unsafe.As>(ref result); } return value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ScalarLane Sign(ScalarLane value) => new((value.value > T.Zero) ? T.One : (value.value < T.Zero) ? ~T.Zero : T.Zero); + public static ScalarLane Sign(ScalarLane value) + { + return new((value.value > TNumber.Zero) ? TNumber.One : (value.value < TNumber.Zero) ? ~TNumber.Zero : TNumber.Zero); + } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ScalarLane CopySign(ScalarLane magnitude, ScalarLane sign) => new(T.CopySign(magnitude.value, sign.value)); + public static ScalarLane CopySign(ScalarLane magnitude, ScalarLane sign) + { + return new(TNumber.CopySign(magnitude.value, sign.value)); + } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ScalarLane Rcp(ScalarLane value) => new(T.One / value.value); + public static ScalarLane Rcp(ScalarLane value) + { + return new(TNumber.One / value.value); + } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ScalarLane Rsqrt(ScalarLane value) => Sqrt(Rcp(value)); + public static ScalarLane Rsqrt(ScalarLane value) + { + return Sqrt(Rcp(value)); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ScalarLane Select(ScalarLane conditionMask, ScalarLane ifTrue, ScalarLane ifFalse) + { + return new(conditionMask.value != TNumber.Zero ? ifTrue.value : ifFalse.value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ScalarLane GreaterThan(ScalarLane a, ScalarLane b) + { + return new(a.value > b.value ? ~TNumber.Zero : TNumber.Zero); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ScalarLane GreaterThanOrEqual(ScalarLane a, ScalarLane b) + { + return new(a.value >= b.value ? ~TNumber.Zero : TNumber.Zero); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ScalarLane LessThan(ScalarLane a, ScalarLane b) + { + return new(a.value < b.value ? ~TNumber.Zero : TNumber.Zero); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ScalarLane LessThanOrEqual(ScalarLane a, ScalarLane b) + { + return new(a.value <= b.value ? ~TNumber.Zero : TNumber.Zero); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ScalarLane Equal(ScalarLane a, ScalarLane b) + { + return new(a.value == b.value ? ~TNumber.Zero : TNumber.Zero); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool Any(ScalarLane mask) + { + return mask.value != TNumber.Zero; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool All(ScalarLane mask) + { + return mask.value != TNumber.Zero; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool None(ScalarLane mask) + { + return mask.value == TNumber.Zero; + } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ScalarLane Select(ScalarLane conditionMask, ScalarLane ifTrue, ScalarLane ifFalse) => new(conditionMask.value != T.Zero ? ifTrue.value : ifFalse.value); + public bool Equals(ScalarLane other) + { + return value.Equals(other.value); + } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ScalarLane GreaterThan(ScalarLane a, ScalarLane b) => new(a.value > b.value ? ~T.Zero : T.Zero); + public override bool Equals(object? obj) + { + return obj is ScalarLane other && value.Equals(other.value); + } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ScalarLane GreaterThanOrEqual(ScalarLane a, ScalarLane b) => new(a.value >= b.value ? ~T.Zero : T.Zero); + public override int GetHashCode() + { + return value.GetHashCode(); + } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ScalarLane LessThan(ScalarLane a, ScalarLane b) => new(a.value < b.value ? ~T.Zero : T.Zero); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ScalarLane LessThanOrEqual(ScalarLane a, ScalarLane b) => new(a.value <= b.value ? ~T.Zero : T.Zero); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ScalarLane Equal(ScalarLane a, ScalarLane b) => new(a.value == b.value ? ~T.Zero : T.Zero); - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool Any(ScalarLane mask) => mask.value != T.Zero; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool All(ScalarLane mask) => mask.value != T.Zero; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool None(ScalarLane mask) => mask.value == T.Zero; - public override string ToString() { return value.ToString() ?? string.Empty; diff --git a/Misaki.HighPerformance.Mathematics.SPMD/ShuffleTableGenerator.cs b/Misaki.HighPerformance.Mathematics.SPMD/ShuffleTableGenerator.cs index 95b118c..b555cd4 100644 --- a/Misaki.HighPerformance.Mathematics.SPMD/ShuffleTableGenerator.cs +++ b/Misaki.HighPerformance.Mathematics.SPMD/ShuffleTableGenerator.cs @@ -2,7 +2,7 @@ using System.Runtime.InteropServices; namespace Misaki.HighPerformance.Mathematics.SPMD; -public static unsafe class ShuffleTableGenerator +internal static unsafe class ShuffleTableGenerator { public static uint* ComputeShuffleTable512_32Bit() { diff --git a/Misaki.HighPerformance.Mathematics.SPMD/Vector3.cs b/Misaki.HighPerformance.Mathematics.SPMD/Vector3.cs new file mode 100644 index 0000000..c0ec93c --- /dev/null +++ b/Misaki.HighPerformance.Mathematics.SPMD/Vector3.cs @@ -0,0 +1,198 @@ +using System.Diagnostics; +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace Misaki.HighPerformance.Mathematics.SPMD; + +public unsafe struct Vector3 : IEquatable> + where TLane : ISPMD + where TNumber : unmanaged, INumber, IMinMaxValue, IBitwiseOperators +{ + public TLane x; + public TLane y; + public TLane z; + + public TLane this[int index] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + RangeCheck(index); + return Unsafe.Add(ref x, index); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [Conditional("ENABLE_COLLECTION_CHECKS")] + private static void RangeCheck(int index) + { + if (index < 0 || index >= 3) + { + throw new IndexOutOfRangeException($"Index {index} is out of range for Vector3."); + } + } + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Store(TNumber* pDst) + { + var width = TLane.LaneWidth; + + var x = stackalloc TNumber[width]; + var y = stackalloc TNumber[width]; + var z = stackalloc TNumber[width]; + + this.x.Store(x); + this.y.Store(y); + this.z.Store(z); + + for (var i = 0; i < width; i++) + { + pDst[i * 3 + 0] = x[i]; + pDst[i * 3 + 1] = y[i]; + pDst[i * 3 + 2] = z[i]; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Store(ref TNumber dst) + { + Store((TNumber*)Unsafe.AsPointer(ref dst)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Store(TNumber* pX, TNumber* pY, TNumber* pZ) + { + x.Store(pX); + y.Store(pY); + z.Store(pZ); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Store(ref TNumber x, ref TNumber y, ref TNumber z) + { + this.x.Store(ref x); + this.y.Store(ref y); + this.z.Store(ref z); + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 operator +(in Vector3 left, in Vector3 right) + { + return new Vector3 + { + x = left.x + right.x, + y = left.y + right.y, + z = left.z + right.z, + }; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 operator +(in Vector3 vector, TLane lane) + { + return new Vector3 + { + x = vector.x + lane, + y = vector.y + lane, + z = vector.z + lane, + }; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 operator -(in Vector3 left, in Vector3 right) + { + return new Vector3 + { + x = left.x - right.x, + y = left.y - right.y, + z = left.z - right.z, + }; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 operator -(in Vector3 vector, TLane lane) + { + return new Vector3 + { + x = vector.x - lane, + y = vector.y - lane, + z = vector.z - lane, + }; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 operator *(in Vector3 left, in Vector3 right) + { + return new Vector3 + { + x = left.x * right.x, + y = left.y * right.y, + z = left.z * right.z, + }; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 operator *(in Vector3 vector, TLane lane) + { + return new Vector3 + { + x = vector.x * lane, + y = vector.y * lane, + z = vector.z * lane, + }; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 operator /(in Vector3 left, in Vector3 right) + { + return new Vector3 + { + x = left.x / right.x, + y = left.y / right.y, + z = left.z / right.z, + }; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 operator == (in Vector3 left, in Vector3 right) + { + return new Vector3 + { + x = left.x == right.x, + y = left.y == right.y, + z = left.z == right.z, + }; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 operator != (in Vector3 left, in Vector3 right) + { + return new Vector3 + { + x = left.x != right.x, + y = left.y != right.y, + z = left.z != right.z, + }; + } + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly bool Equals(Vector3 other) + { + return x.Equals(other.x) && y.Equals(other.y) && z.Equals(other.z); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override readonly bool Equals(object? obj) + { + return obj is Vector3 other && Equals(other); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override int GetHashCode() + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/Misaki.HighPerformance.Mathematics.SPMD/WideLane.cs b/Misaki.HighPerformance.Mathematics.SPMD/WideLane.cs index 9f3cf48..c5003c7 100644 --- a/Misaki.HighPerformance.Mathematics.SPMD/WideLane.cs +++ b/Misaki.HighPerformance.Mathematics.SPMD/WideLane.cs @@ -6,67 +6,22 @@ using System.Runtime.Intrinsics.X86; namespace Misaki.HighPerformance.Mathematics.SPMD; -[StructLayout(LayoutKind.Sequential)] -public readonly unsafe struct WideLane : ISPMD, T> - where T : unmanaged, INumber, IMinMaxValue, IBitwiseOperators +public static unsafe class WideLane { - private static readonly Vector s_indices; + internal static readonly uint* s_shuffleTable512_32bit; + internal static readonly ulong* s_shuffleTable512_64bit; + internal static readonly uint* s_shuffleTable256_32bit; + internal static readonly ulong* s_shuffleTable256_64bit; + internal static readonly uint* s_shuffleTable128_32bit; + internal static readonly ulong* s_shuffleTable128_64bit; - private static readonly uint* s_shuffleTable512_32bit; - private static readonly ulong* s_shuffleTable512_64bit; - private static readonly uint* s_shuffleTable256_32bit; - private static readonly ulong* s_shuffleTable256_64bit; - private static readonly uint* s_shuffleTable128_32bit; - private static readonly ulong* s_shuffleTable128_64bit; - - public readonly Vector value; - - public static int LaneWidth - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => Vector.Count; - } - - public static WideLane Zero - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => new(Vector.Zero); - } - - public static WideLane One - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => new(Vector.One); - } - - public static WideLane MinValue - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => Create(T.MinValue); - } - - public static WideLane MaxValue - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => Create(T.MaxValue); - } - - public readonly T this[int index] - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => value[index]; - } + /// + /// Gets whether WideLane is supported on the current hardware. + /// + public static bool IsSupported => Vector.IsHardwareAccelerated; static WideLane() { - var pValues = stackalloc T[LaneWidth]; - for (var i = 0; i < LaneWidth; i++) - { - pValues[i] = T.CreateChecked(i); - } - - s_indices = Vector.Load(pValues); - s_shuffleTable512_32bit = ShuffleTableGenerator.ComputeShuffleTable512_32Bit(); s_shuffleTable512_64bit = ShuffleTableGenerator.ComputeShuffleTable512_64Bit(); s_shuffleTable256_32bit = ShuffleTableGenerator.ComputeShuffleTable256_32Bit(); @@ -74,103 +29,177 @@ public readonly unsafe struct WideLane : ISPMD, T> s_shuffleTable128_32bit = ShuffleTableGenerator.ComputeShuffleTable128_32Bit(); s_shuffleTable128_64bit = ShuffleTableGenerator.ComputeShuffleTable128_64Bit(); } +} - public WideLane(Vector value) +[StructLayout(LayoutKind.Sequential)] +public readonly unsafe struct WideLane : ISPMD, TNumber> + where TNumber : unmanaged, INumber, IMinMaxValue, IBitwiseOperators +{ + private static readonly Vector s_indices; + + public readonly Vector value; + + public static int LaneWidth + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => Vector.Count; + } + + public static WideLane Zero + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new(Vector.Zero); + } + + public static WideLane One + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new(Vector.One); + } + + public static WideLane MinValue + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => Create(TNumber.MinValue); + } + + public static WideLane MaxValue + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => Create(TNumber.MaxValue); + } + + public readonly TNumber this[int index] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => value[index]; + } + + static WideLane() + { + var pValues = stackalloc TNumber[LaneWidth]; + for (var i = 0; i < LaneWidth; i++) + { + pValues[i] = TNumber.CreateChecked(i); + } + + s_indices = Vector.Load(pValues); + } + + public WideLane(Vector value) { this.value = value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Vector VectorFloor(Vector vector) + private static Vector VectorFloor(Vector vector) { - if (typeof(T) == typeof(float)) + if (typeof(TNumber) == typeof(float)) { - ref var v = ref Unsafe.As, Vector>(ref vector); + ref var v = ref Unsafe.As, Vector>(ref vector); var floored = Vector.Floor(v); - return Unsafe.As, Vector>(ref floored); + return Unsafe.As, Vector>(ref floored); } - else if (typeof(T) == typeof(double)) + else if (typeof(TNumber) == typeof(double)) { - ref var v = ref Unsafe.As, Vector>(ref vector); + ref var v = ref Unsafe.As, Vector>(ref vector); var floored = Vector.Floor(v); - return Unsafe.As, Vector>(ref floored); + return Unsafe.As, Vector>(ref floored); } return vector; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Vector VectorTruncate(Vector vector) + private static Vector VectorTruncate(Vector vector) { - if (typeof(T) == typeof(float)) + if (typeof(TNumber) == typeof(float)) { - ref var v = ref Unsafe.As, Vector>(ref vector); + ref var v = ref Unsafe.As, Vector>(ref vector); var truncated = Vector.Truncate(v); - return Unsafe.As, Vector>(ref truncated); + return Unsafe.As, Vector>(ref truncated); } - else if (typeof(T) == typeof(double)) + else if (typeof(TNumber) == typeof(double)) { - ref var v = ref Unsafe.As, Vector>(ref vector); + ref var v = ref Unsafe.As, Vector>(ref vector); var truncated = Vector.Truncate(v); - return Unsafe.As, Vector>(ref truncated); + return Unsafe.As, Vector>(ref truncated); } return vector; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static WideLane Create(T value) => new(Vector.Create(value)); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static WideLane Create(params ReadOnlySpan values) => new(Vector.Create(values)); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static WideLane Create(Vector value) => new(value); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static WideLane Sequence(T start, T step) => new(Vector.Create(start) + (Vector.Create(step) * s_indices)); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static WideLane Load(ref T value) => new(Vector.LoadUnsafe(ref value)); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static WideLane Load(T* pValue) => new(Vector.Load(pValue)); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static WideLane CastFrom(WideLane value) - where U : unmanaged, INumber, IMinMaxValue, IBitwiseOperators + public static WideLane Create(TNumber value) { - return new(Unsafe.As, Vector>(ref value)); + return new(Vector.Create(value)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly void Store(ref T destination) => value.StoreUnsafe(ref destination); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly void Store(T* pDestination) => value.Store(pDestination); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int CompressStore(WideLane mask, ref T destination) + public static WideLane Create(params ReadOnlySpan values) { - return CompressStore(mask, (T*)Unsafe.AsPointer(in destination)); + return new(Vector.Create(values)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int CompressStore(WideLane mask, T* pDestination) + public static WideLane Create(Vector value) { - var size = sizeof(T); + return new(value); + } - if (LaneWidth == Vector512.Count && Vector512.IsHardwareAccelerated) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static WideLane Sequence(TNumber start, TNumber step) + { + return new(Vector.Create(start) + (Vector.Create(step) * s_indices)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static WideLane Load(ref TNumber value) + { + return new(Vector.LoadUnsafe(ref value)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static WideLane Load(TNumber* pValue) + { + return new(Vector.Load(pValue)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly void Store(ref TNumber destination) + { + value.StoreUnsafe(ref destination); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly void Store(TNumber* pDestination) + { + value.Store(pDestination); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int CompressStore(WideLane mask, ref TNumber destination) + { + return CompressStore(mask, (TNumber*)Unsafe.AsPointer(in destination)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int CompressStore(WideLane mask, TNumber* pDestination) + { + var size = sizeof(TNumber); + + if (LaneWidth == Vector512.Count && Vector512.IsHardwareAccelerated) { if (size == 4) { - ref var vec = ref Unsafe.As, Vector512>(ref Unsafe.AsRef(in this)); - var m = Unsafe.As, Vector512>(ref mask); + ref var vec = ref Unsafe.As, Vector512>(ref Unsafe.AsRef(in this)); + var m = Unsafe.As, Vector512>(ref mask); var moveMask = m.ExtractMostSignificantBits(); // Offset is (moveMask * 16) because each control vector has 16 elements - var shuffle = Vector512.Load(s_shuffleTable512_32bit + (moveMask * 16)); + var shuffle = Vector512.Load(WideLane.s_shuffleTable512_32bit + (moveMask * 16)); var compressed = Vector512.Shuffle(vec, shuffle); compressed.Store((uint*)pDestination); @@ -179,28 +208,28 @@ public readonly unsafe struct WideLane : ISPMD, T> if (size == 8) { - ref var vec = ref Unsafe.As, Vector512>(ref Unsafe.AsRef(in this)); - var m = Unsafe.As, Vector512>(ref mask); + ref var vec = ref Unsafe.As, Vector512>(ref Unsafe.AsRef(in this)); + var m = Unsafe.As, Vector512>(ref mask); var moveMask = m.ExtractMostSignificantBits(); // Offset is (moveMask * 8) because each control vector has 8 elements - var shuffle = Vector512.Load(s_shuffleTable512_64bit + (moveMask * 8)); + var shuffle = Vector512.Load(WideLane.s_shuffleTable512_64bit + (moveMask * 8)); var compressed = Vector512.Shuffle(vec, shuffle); compressed.Store((ulong*)pDestination); return BitOperations.PopCount(moveMask); } } - else if (LaneWidth == Vector256.Count && Vector256.IsHardwareAccelerated) + else if (LaneWidth == Vector256.Count && Vector256.IsHardwareAccelerated) { if (size == 4) { - ref var vec = ref Unsafe.As, Vector256>(ref Unsafe.AsRef(in this)); - var m = Unsafe.As, Vector256>(ref mask); + ref var vec = ref Unsafe.As, Vector256>(ref Unsafe.AsRef(in this)); + var m = Unsafe.As, Vector256>(ref mask); var moveMask = m.ExtractMostSignificantBits(); // Offset is (moveMask * 8) because each control vector has 8 elements - var shuffle = Vector256.Load(s_shuffleTable256_32bit + (moveMask * 8)); + var shuffle = Vector256.Load(WideLane.s_shuffleTable256_32bit + (moveMask * 8)); var compressed = Vector256.Shuffle(vec, shuffle); compressed.Store((uint*)pDestination); @@ -209,30 +238,30 @@ public readonly unsafe struct WideLane : ISPMD, T> if (size == 8) { - ref var vec = ref Unsafe.As, Vector256>(ref Unsafe.AsRef(in this)); - var m = Unsafe.As, Vector256>(ref mask); + ref var vec = ref Unsafe.As, Vector256>(ref Unsafe.AsRef(in this)); + var m = Unsafe.As, Vector256>(ref mask); // For 64-bit, ExtractMostSignificantBits only populates 4 bits (0-15) var moveMask = m.ExtractMostSignificantBits(); // Offset is (moveMask * 4) because each control vector has 4 elements - var shuffle = Vector256.Load(s_shuffleTable256_64bit + (moveMask * 4)); + var shuffle = Vector256.Load(WideLane.s_shuffleTable256_64bit + (moveMask * 4)); var compressed = Vector256.Shuffle(vec, shuffle); compressed.Store((ulong*)pDestination); return BitOperations.PopCount(moveMask); } } - else if (LaneWidth == Vector128.Count && Vector128.IsHardwareAccelerated) + else if (LaneWidth == Vector128.Count && Vector128.IsHardwareAccelerated) { if (size == 4) { - ref var vec = ref Unsafe.As, Vector128>(ref Unsafe.AsRef(in this)); - var m = Unsafe.As, Vector128>(ref mask); + ref var vec = ref Unsafe.As, Vector128>(ref Unsafe.AsRef(in this)); + var m = Unsafe.As, Vector128>(ref mask); var moveMask = m.ExtractMostSignificantBits(); // Offset is (moveMask * 4) because each control vector has 4 elements - var shuffle = Vector128.Load(s_shuffleTable128_32bit + (moveMask * 4)); + var shuffle = Vector128.Load(WideLane.s_shuffleTable128_32bit + (moveMask * 4)); var compressed = Vector128.Shuffle(vec, shuffle); compressed.Store((uint*)pDestination); @@ -241,11 +270,11 @@ public readonly unsafe struct WideLane : ISPMD, T> if (size == 8) { - ref var vec = ref Unsafe.As, Vector128>(ref Unsafe.AsRef(in this)); - var m = Unsafe.As, Vector128>(ref mask); + ref var vec = ref Unsafe.As, Vector128>(ref Unsafe.AsRef(in this)); + var m = Unsafe.As, Vector128>(ref mask); var moveMask = m.ExtractMostSignificantBits(); // Offset is (moveMask * 2) because each control vector has 2 elements - var shuffle = Vector128.Load(s_shuffleTable128_64bit + (moveMask * 2)); + var shuffle = Vector128.Load(WideLane.s_shuffleTable128_64bit + (moveMask * 2)); var compressed = Vector128.Shuffle(vec, shuffle); compressed.Store((ulong*)pDestination); return BitOperations.PopCount(moveMask); @@ -257,7 +286,7 @@ public readonly unsafe struct WideLane : ISPMD, T> var count = 0; for (var i = 0; i < LaneWidth; i++) { - if (mask.value[i] == ~T.Zero) + if (mask.value[i] == ~TNumber.Zero) { pDestination[count++] = value[i]; } @@ -267,113 +296,152 @@ public readonly unsafe struct WideLane : ISPMD, T> } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly Vector AsVector() => value; - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static WideLane operator +(WideLane a, WideLane b) => new(a.value + b.value); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static WideLane operator +(WideLane a, T b) => new(a.value + Vector.Create(b)); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static WideLane operator -(WideLane a, WideLane b) => new(a.value - b.value); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static WideLane operator -(WideLane a, T b) => new(a.value - Vector.Create(b)); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static WideLane operator *(WideLane a, WideLane b) => new(a.value * b.value); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static WideLane operator *(WideLane a, T b) => new(a.value * Vector.Create(b)); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static WideLane operator /(WideLane a, WideLane b) => new(a.value / b.value); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static WideLane operator /(WideLane a, T b) => new(a.value / Vector.Create(b)); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static WideLane operator %(WideLane a, WideLane b) => new(a.value - VectorFloor(a.value / b.value) * b.value); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static WideLane operator %(WideLane a, T b) + public readonly Vector AsVector() { - var vb = Vector.Create(b); - return new(a.value - VectorFloor(a.value / vb) * vb); + return value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static WideLane operator -(WideLane a) => new(-a.value); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static WideLane operator &(WideLane a, WideLane b) => new(a.value & b.value); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static WideLane operator &(WideLane a, T b) => new(a.value & Vector.Create(b)); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static WideLane operator |(WideLane a, WideLane b) => new(a.value | b.value); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static WideLane operator |(WideLane a, T b) => new(a.value | Vector.Create(b)); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static WideLane operator ^(WideLane a, WideLane b) => new(a.value ^ b.value); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static WideLane operator ^(WideLane a, T b) => new(a.value ^ Vector.Create(b)); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static WideLane operator ~(WideLane a) => new(~a.value); - - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static WideLane Abs(WideLane value) => new(Vector.Abs(value.value)); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static WideLane Floor(WideLane value) + public static WideLane operator +(WideLane a, WideLane b) { - if (typeof(T) == typeof(float)) + return new(a.value + b.value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static WideLane operator -(WideLane a, WideLane b) + { + return new(a.value - b.value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static WideLane operator *(WideLane a, WideLane b) + { + return new(a.value * b.value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static WideLane operator /(WideLane a, WideLane b) + { + return new(a.value / b.value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static WideLane operator %(WideLane a, WideLane b) + { + return new(a.value - VectorFloor(a.value / b.value) * b.value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static WideLane operator -(WideLane a) + { + return new(-a.value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static WideLane operator &(WideLane a, WideLane b) + { + return new(a.value & b.value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static WideLane operator |(WideLane a, WideLane b) + { + return new(a.value | b.value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static WideLane operator ^(WideLane a, WideLane b) + { + return new(a.value ^ b.value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static WideLane operator ~(WideLane a) + { + return new(~a.value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static WideLane operator ==(WideLane a, WideLane b) + { + return Equal(a, b); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static WideLane operator !=(WideLane a, WideLane b) + { + return ~Equal(a, b); + } + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator WideLane(TNumber value) + { + return Create(value); + } + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static WideLane Abs(WideLane value) + { + return new(Vector.Abs(value.value)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static WideLane Floor(WideLane value) + { + if (typeof(TNumber) == typeof(float)) { - ref var v = ref Unsafe.As, Vector>(ref value); + ref var v = ref Unsafe.As, Vector>(ref value); var floored = Vector.Floor(v); - return new WideLane(Unsafe.As, Vector>(ref floored)); + return new WideLane(Unsafe.As, Vector>(ref floored)); } - else if (typeof(T) == typeof(double)) + else if (typeof(TNumber) == typeof(double)) { - ref var v = ref Unsafe.As, Vector>(ref value); + ref var v = ref Unsafe.As, Vector>(ref value); var floored = Vector.Floor(v); - return new WideLane(Unsafe.As, Vector>(ref floored)); + return new WideLane(Unsafe.As, Vector>(ref floored)); } return value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static WideLane Frac(WideLane value) => new(value.value - VectorFloor(value.value)); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static WideLane Sqrt(WideLane value) => new(Vector.SquareRoot(value.value)); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static WideLane Lerp(WideLane a, WideLane b, WideLane t) => new(a.value + (b.value - a.value) * t.value); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static WideLane MultipleAdd(WideLane a, WideLane b, WideLane c) + public static WideLane Frac(WideLane value) { - if (typeof(T) == typeof(float)) + return new(value.value - VectorFloor(value.value)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static WideLane Sqrt(WideLane value) + { + return new(Vector.SquareRoot(value.value)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static WideLane Lerp(WideLane a, WideLane b, WideLane t) + { + return new(a.value + (b.value - a.value) * t.value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static WideLane MultipleAdd(WideLane a, WideLane b, WideLane c) + { + if (typeof(TNumber) == typeof(float)) { - ref var va = ref Unsafe.As, Vector>(ref a); - ref var vb = ref Unsafe.As, Vector>(ref b); - ref var vc = ref Unsafe.As, Vector>(ref c); + ref var va = ref Unsafe.As, Vector>(ref a); + ref var vb = ref Unsafe.As, Vector>(ref b); + ref var vc = ref Unsafe.As, Vector>(ref c); var result = Vector.FusedMultiplyAdd(va, vb, vc); - return new WideLane(Unsafe.As, Vector>(ref result)); + return new WideLane(Unsafe.As, Vector>(ref result)); } - else if (typeof(T) == typeof(double)) + else if (typeof(TNumber) == typeof(double)) { - ref var va = ref Unsafe.As, Vector>(ref a); - ref var vb = ref Unsafe.As, Vector>(ref b); - ref var vc = ref Unsafe.As, Vector>(ref c); + ref var va = ref Unsafe.As, Vector>(ref a); + ref var vb = ref Unsafe.As, Vector>(ref b); + ref var vc = ref Unsafe.As, Vector>(ref c); var result = Vector.FusedMultiplyAdd(va, vb, vc); - return new WideLane(Unsafe.As, Vector>(ref result)); + return new WideLane(Unsafe.As, Vector>(ref result)); } else { @@ -382,83 +450,95 @@ public readonly unsafe struct WideLane : ISPMD, T> } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static WideLane Min(WideLane a, WideLane b) => new(Vector.Min(a.value, b.value)); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static WideLane Max(WideLane a, WideLane b) => new(Vector.Max(a.value, b.value)); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static WideLane Clamp(WideLane value, WideLane min, WideLane max) => new(Vector.Clamp(value.value, min.value, max.value)); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static WideLane Saturate(WideLane value) => Clamp(value, Create(T.Zero), Create(T.One)); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static WideLane Sin(WideLane value) + public static WideLane Min(WideLane a, WideLane b) { - if (typeof(T) == typeof(float)) + return new(Vector.Min(a.value, b.value)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static WideLane Max(WideLane a, WideLane b) + { + return new(Vector.Max(a.value, b.value)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static WideLane Clamp(WideLane value, WideLane min, WideLane max) + { + return new(Vector.Clamp(value.value, min.value, max.value)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static WideLane Saturate(WideLane value) + { + return Clamp(value, Create(TNumber.Zero), Create(TNumber.One)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static WideLane Sin(WideLane value) + { + if (typeof(TNumber) == typeof(float)) { - ref var v = ref Unsafe.As, Vector>(ref value); + ref var v = ref Unsafe.As, Vector>(ref value); var result = Vector.Sin(v); - return new WideLane(Unsafe.As, Vector>(ref result)); + return new WideLane(Unsafe.As, Vector>(ref result)); } - else if (typeof(T) == typeof(double)) + else if (typeof(TNumber) == typeof(double)) { - ref var v = ref Unsafe.As, Vector>(ref value); + ref var v = ref Unsafe.As, Vector>(ref value); var result = Vector.Sin(v); - return new WideLane(Unsafe.As, Vector>(ref result)); + return new WideLane(Unsafe.As, Vector>(ref result)); } return value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static WideLane Cos(WideLane value) + public static WideLane Cos(WideLane value) { - if (typeof(T) == typeof(float)) + if (typeof(TNumber) == typeof(float)) { - ref var v = ref Unsafe.As, Vector>(ref value); + ref var v = ref Unsafe.As, Vector>(ref value); var result = Vector.Cos(v); - return new WideLane(Unsafe.As, Vector>(ref result)); + return new WideLane(Unsafe.As, Vector>(ref result)); } - else if (typeof(T) == typeof(double)) + else if (typeof(TNumber) == typeof(double)) { - ref var v = ref Unsafe.As, Vector>(ref value); + ref var v = ref Unsafe.As, Vector>(ref value); var result = Vector.Cos(v); - return new WideLane(Unsafe.As, Vector>(ref result)); + return new WideLane(Unsafe.As, Vector>(ref result)); } return value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static (WideLane sin, WideLane cos) SinCos(WideLane value) + public static (WideLane sin, WideLane cos) SinCos(WideLane value) { - if (typeof(T) == typeof(float)) + if (typeof(TNumber) == typeof(float)) { - ref var v = ref Unsafe.As, Vector>(ref value); + ref var v = ref Unsafe.As, Vector>(ref value); var (sin, cos) = Vector.SinCos(v); - return (new WideLane(Unsafe.As, Vector>(ref sin)), new WideLane(Unsafe.As, Vector>(ref cos))); + return (new WideLane(Unsafe.As, Vector>(ref sin)), new WideLane(Unsafe.As, Vector>(ref cos))); } - else if (typeof(T) == typeof(double)) + else if (typeof(TNumber) == typeof(double)) { - ref var v = ref Unsafe.As, Vector>(ref value); + ref var v = ref Unsafe.As, Vector>(ref value); var (sin, cos) = Vector.SinCos(v); - return (new WideLane(Unsafe.As, Vector>(ref sin)), new WideLane(Unsafe.As, Vector>(ref cos))); + return (new WideLane(Unsafe.As, Vector>(ref sin)), new WideLane(Unsafe.As, Vector>(ref cos))); } return (value, value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static WideLane Tan(WideLane value) + public static WideLane Tan(WideLane value) { // 1. Range Reduction // Transform value into range [-pi/4, pi/4]. // This is complex to do right (Payne-Hanek), but for games // a simple approximation: value = value - (PI * Round(value / PI)) is good enough. - var pi = Create(T.CreateChecked(Math.PI)); + var pi = Create(TNumber.CreateChecked(Math.PI)); var x = value - pi * Round(value / pi); // 2. The Approximation (Remez Polynomial) @@ -466,8 +546,8 @@ public readonly unsafe struct WideLane : ISPMD, T> // Factored (Horner's Method) for fewer ops: value * (1 + value^2 * (c1 + c2*value^2)) var x2 = x * x; - var vc1 = Create(T.CreateChecked(0.3333314036)); // 1/3 - var vc2 = Create(T.CreateChecked(0.1333923995)); // 2/15 + var vc1 = Create(TNumber.CreateChecked(0.3333314036)); // 1/3 + var vc2 = Create(TNumber.CreateChecked(0.1333923995)); // 2/15 // x2 * (c1 + c2 * x2) var poly = MultipleAdd(x2, vc2, vc1); @@ -476,26 +556,26 @@ public readonly unsafe struct WideLane : ISPMD, T> } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static WideLane Asin(WideLane value) + public static WideLane Asin(WideLane value) { // asin(value) = pi/2 - acos(value) - var piOver2 = Create(T.CreateChecked(Math.PI / 2)); + var piOver2 = Create(TNumber.CreateChecked(Math.PI / 2)); return piOver2 - Acos(value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static WideLane Acos(WideLane value) + public static WideLane Acos(WideLane value) { // 0 <= value <= 1 : acos(value) = sqrt(1 - value) * (c0 + c1*value + c2*value^2 + c3*value^3) // value < 0 : acos(value) = pi - acos(-value) var x = Abs(value); - var c0 = Create(T.CreateChecked(1.5707288f)); // pi/2 - var c1 = Create(T.CreateChecked(-0.2121144f)); - var c2 = Create(T.CreateChecked(0.0742610f)); - var c3 = Create(T.CreateChecked(-0.0187293f)); + var c0 = Create(TNumber.CreateChecked(1.5707288f)); // pi/2 + var c1 = Create(TNumber.CreateChecked(-0.2121144f)); + var c2 = Create(TNumber.CreateChecked(0.0742610f)); + var c3 = Create(TNumber.CreateChecked(-0.0187293f)); var term1 = MultipleAdd(x, c3, c2); var term2 = MultipleAdd(x, term1, c1); @@ -504,19 +584,19 @@ public readonly unsafe struct WideLane : ISPMD, T> var sqrtTerm = Sqrt(One - x); var result = poly * sqrtTerm; - var pi = Create(T.CreateChecked(Math.PI)); + var pi = Create(TNumber.CreateChecked(Math.PI)); var isNegative = LessThan(value, Zero); return Select(isNegative, pi - result, result); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static WideLane Atan(WideLane value) + public static WideLane Atan(WideLane value) { // atan(value) = value * (c1 + c2*value^2) - var c1 = Create(T.CreateChecked(0.97239411f)); - var c2 = Create(T.CreateChecked(-0.19194795f)); + var c1 = Create(TNumber.CreateChecked(0.97239411f)); + var c2 = Create(TNumber.CreateChecked(-0.19194795f)); var x2 = value * value; var poly = MultipleAdd(x2, c2, c1); @@ -524,7 +604,7 @@ public readonly unsafe struct WideLane : ISPMD, T> } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static WideLane Atan2(WideLane y, WideLane x) + public static WideLane Atan2(WideLane y, WideLane x) { var absX = Abs(x); var absY = Abs(y); @@ -542,8 +622,8 @@ public readonly unsafe struct WideLane : ISPMD, T> var t2 = t * t; // 2. Polynomial Approximation (Odd function: value * (c1 + c2*value^2)) - var c1 = Create(T.CreateChecked(0.97239411f)); - var c2 = Create(T.CreateChecked(-0.19194795f)); + var c1 = Create(TNumber.CreateChecked(0.97239411f)); + var c2 = Create(TNumber.CreateChecked(-0.19194795f)); // (c1 + c2 * t2) var poly = MultipleAdd(c2, t2, c1); @@ -553,12 +633,12 @@ public readonly unsafe struct WideLane : ISPMD, T> // 3. Reconstruct the angle // If we swapped value/y (yGtX), the identity is: atan(value/y) = PI/2 - atan(y/value) - var halfPi = Create(T.CreateChecked(1.570796327f)); + var halfPi = Create(TNumber.CreateChecked(1.570796327f)); result = Select(yGtX, halfPi - result, result); // 4. Adjust for Quadrants (Signs) // If value < 0, we are in quadrants 2 or 3, so we need to add PI - var pi = Create(T.CreateChecked(3.141592654f)); + var pi = Create(TNumber.CreateChecked(3.141592654f)); var xLtZero = LessThan(x, Zero); result = Select(xLtZero, pi - result, result); @@ -573,220 +653,273 @@ public readonly unsafe struct WideLane : ISPMD, T> } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static WideLane Pow(WideLane x, WideLane y) => Exp(y * Log(x)); + public static WideLane Pow(WideLane x, WideLane y) + { + return Exp(y * Log(x)); + } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static WideLane Exp(WideLane value) + public static WideLane Exp(WideLane value) { - if (typeof(T) == typeof(float)) + if (typeof(TNumber) == typeof(float)) { - ref var v = ref Unsafe.As, Vector>(ref value); + ref var v = ref Unsafe.As, Vector>(ref value); var result = Vector.Exp(v); - return new WideLane(Unsafe.As, Vector>(ref result)); + return new WideLane(Unsafe.As, Vector>(ref result)); } - else if (typeof(T) == typeof(double)) + else if (typeof(TNumber) == typeof(double)) { - ref var v = ref Unsafe.As, Vector>(ref value); + ref var v = ref Unsafe.As, Vector>(ref value); var result = Vector.Exp(v); - return new WideLane(Unsafe.As, Vector>(ref result)); + return new WideLane(Unsafe.As, Vector>(ref result)); } return value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static WideLane Exp2(WideLane value) + public static WideLane Exp2(WideLane value) { - return Pow(Create(T.CreateChecked(2)), value); + return Pow(Create(TNumber.CreateChecked(2)), value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static WideLane Log(WideLane value) + public static WideLane Log(WideLane value) { - if (typeof(T) == typeof(float)) + if (typeof(TNumber) == typeof(float)) { - ref var v = ref Unsafe.As, Vector>(ref value); + ref var v = ref Unsafe.As, Vector>(ref value); var result = Vector.Log(v); - return new WideLane(Unsafe.As, Vector>(ref result)); + return new WideLane(Unsafe.As, Vector>(ref result)); } - else if (typeof(T) == typeof(double)) + else if (typeof(TNumber) == typeof(double)) { - ref var v = ref Unsafe.As, Vector>(ref value); + ref var v = ref Unsafe.As, Vector>(ref value); var result = Vector.Log(v); - return new WideLane(Unsafe.As, Vector>(ref result)); + return new WideLane(Unsafe.As, Vector>(ref result)); } return value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static WideLane Log2(WideLane value) + public static WideLane Log2(WideLane value) { - if (typeof(T) == typeof(float)) + if (typeof(TNumber) == typeof(float)) { - ref var v = ref Unsafe.As, Vector>(ref value); + ref var v = ref Unsafe.As, Vector>(ref value); var result = Vector.Log2(v); - return new WideLane(Unsafe.As, Vector>(ref result)); + return new WideLane(Unsafe.As, Vector>(ref result)); } - else if (typeof(T) == typeof(double)) + else if (typeof(TNumber) == typeof(double)) { - ref var v = ref Unsafe.As, Vector>(ref value); + ref var v = ref Unsafe.As, Vector>(ref value); var result = Vector.Log2(v); - return new WideLane(Unsafe.As, Vector>(ref result)); + return new WideLane(Unsafe.As, Vector>(ref result)); } return value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static WideLane Ceil(WideLane value) + public static WideLane Ceil(WideLane value) { - if (typeof(T) == typeof(float)) + if (typeof(TNumber) == typeof(float)) { - ref var v = ref Unsafe.As, Vector>(ref value); + ref var v = ref Unsafe.As, Vector>(ref value); var result = Vector.Ceiling(v); - return new WideLane(Unsafe.As, Vector>(ref result)); + return new WideLane(Unsafe.As, Vector>(ref result)); } - else if (typeof(T) == typeof(double)) + else if (typeof(TNumber) == typeof(double)) { - ref var v = ref Unsafe.As, Vector>(ref value); + ref var v = ref Unsafe.As, Vector>(ref value); var result = Vector.Ceiling(v); - return new WideLane(Unsafe.As, Vector>(ref result)); + return new WideLane(Unsafe.As, Vector>(ref result)); } return value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static WideLane Round(WideLane value) + public static WideLane Round(WideLane value) { - if (typeof(T) == typeof(float)) + if (typeof(TNumber) == typeof(float)) { - ref var v = ref Unsafe.As, Vector>(ref value); + ref var v = ref Unsafe.As, Vector>(ref value); var result = Vector.Round(v); - return new WideLane(Unsafe.As, Vector>(ref result)); + return new WideLane(Unsafe.As, Vector>(ref result)); } - else if (typeof(T) == typeof(double)) + else if (typeof(TNumber) == typeof(double)) { - ref var v = ref Unsafe.As, Vector>(ref value); + ref var v = ref Unsafe.As, Vector>(ref value); var result = Vector.Round(v); - return new WideLane(Unsafe.As, Vector>(ref result)); + return new WideLane(Unsafe.As, Vector>(ref result)); } return value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static WideLane Trunc(WideLane value) + public static WideLane Trunc(WideLane value) { - if (typeof(T) == typeof(float)) + if (typeof(TNumber) == typeof(float)) { - ref var v = ref Unsafe.As, Vector>(ref value); + ref var v = ref Unsafe.As, Vector>(ref value); var result = Vector.Truncate(v); - return new WideLane(Unsafe.As, Vector>(ref result)); + return new WideLane(Unsafe.As, Vector>(ref result)); } - else if (typeof(T) == typeof(double)) + else if (typeof(TNumber) == typeof(double)) { - ref var v = ref Unsafe.As, Vector>(ref value); + ref var v = ref Unsafe.As, Vector>(ref value); var result = Vector.Truncate(v); - return new WideLane(Unsafe.As, Vector>(ref result)); + return new WideLane(Unsafe.As, Vector>(ref result)); } return value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static WideLane Sign(WideLane value) => Select( + public static WideLane Sign(WideLane value) + { + return Select( GreaterThan(value, Zero), One, Select( LessThan(value, Zero), ~Zero, Zero)); + } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static WideLane CopySign(WideLane magnitude, WideLane sign) => new(Vector.CopySign(magnitude.value, sign.value)); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static WideLane Rcp(WideLane value) + public static WideLane CopySign(WideLane magnitude, WideLane sign) { - if (typeof(T) == typeof(float)) + return new(Vector.CopySign(magnitude.value, sign.value)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static WideLane Rcp(WideLane value) + { + if (typeof(TNumber) == typeof(float)) { if (Sse.IsSupported && LaneWidth == Vector128.Count) { - var vf = Unsafe.As, Vector128>(ref value); + var vf = Unsafe.As, Vector128>(ref value); var result = Sse.Reciprocal(vf); - return Unsafe.As, WideLane>(ref result); + return Unsafe.As, WideLane>(ref result); } else if (Avx.IsSupported && LaneWidth == Vector256.Count) { - var vf = Unsafe.As, Vector256>(ref value); + var vf = Unsafe.As, Vector256>(ref value); var result = Avx.Reciprocal(vf); - return Unsafe.As, WideLane>(ref result); + return Unsafe.As, WideLane>(ref result); } } - return Create(T.One) / value; + return Create(TNumber.One) / value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static WideLane Rsqrt(WideLane value) + public static WideLane Rsqrt(WideLane value) { - if (typeof(T) == typeof(float)) + if (typeof(TNumber) == typeof(float)) { if (Sse.IsSupported && LaneWidth == Vector128.Count) { - var vf = Unsafe.As, Vector128>(ref value); + var vf = Unsafe.As, Vector128>(ref value); var result = Sse.ReciprocalSqrt(vf); - return Unsafe.As, WideLane>(ref result); + return Unsafe.As, WideLane>(ref result); } else if (Avx.IsSupported && LaneWidth == Vector256.Count) { - var vf = Unsafe.As, Vector256>(ref value); + var vf = Unsafe.As, Vector256>(ref value); var result = Avx.ReciprocalSqrt(vf); - return Unsafe.As, WideLane>(ref result); + return Unsafe.As, WideLane>(ref result); } } - return Create(T.One) / Sqrt(value); + return Create(TNumber.One) / Sqrt(value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static WideLane Select(WideLane conditionMask, WideLane ifTrue, WideLane ifFalse) - => new(Vector.ConditionalSelect( - conditionMask.value, - ifTrue.value, - ifFalse.value)); + public static WideLane Select(WideLane conditionMask, WideLane ifTrue, WideLane ifFalse) + { + return new(Vector.ConditionalSelect( + conditionMask.value, + ifTrue.value, + ifFalse.value)); + } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static WideLane GreaterThan(WideLane a, WideLane b) => new(Vector.GreaterThan(a.value, b.value)); + public static WideLane GreaterThan(WideLane a, WideLane b) + { + return new(Vector.GreaterThan(a.value, b.value)); + } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static WideLane GreaterThanOrEqual(WideLane a, WideLane b) => new(Vector.GreaterThanOrEqual(a.value, b.value)); + public static WideLane GreaterThanOrEqual(WideLane a, WideLane b) + { + return new(Vector.GreaterThanOrEqual(a.value, b.value)); + } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static WideLane LessThan(WideLane a, WideLane b) => new(Vector.LessThan(a.value, b.value)); + public static WideLane LessThan(WideLane a, WideLane b) + { + return new(Vector.LessThan(a.value, b.value)); + } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static WideLane LessThanOrEqual(WideLane a, WideLane b) => new(Vector.LessThanOrEqual(a.value, b.value)); + public static WideLane LessThanOrEqual(WideLane a, WideLane b) + { + return new(Vector.LessThanOrEqual(a.value, b.value)); + } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static WideLane Equal(WideLane a, WideLane b) => new(Vector.Equals(a.value, b.value)); + public static WideLane Equal(WideLane a, WideLane b) + { + return new(Vector.Equals(a.value, b.value)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool Any(WideLane mask) + { + return !Vector.EqualsAll(mask.value, Vector.Zero); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool All(WideLane mask) + { + return Vector.EqualsAll(mask.value, Vector.AllBitsSet); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool None(WideLane mask) + { + return Vector.EqualsAll(mask.value, Vector.Zero); + } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool Any(WideLane mask) => !Vector.EqualsAll(mask.value, Vector.Zero); + public bool Equals(WideLane other) + { + return value.Equals(other.value); + } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool All(WideLane mask) => Vector.EqualsAll(mask.value, Vector.AllBitsSet); + public override bool Equals(object? obj) + { + return obj is WideLane other && Equals(other); + } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool None(WideLane mask) => Vector.EqualsAll(mask.value, Vector.Zero); - + public override int GetHashCode() + { + return value.GetHashCode(); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public override string ToString() { return value.ToString(); diff --git a/Misaki.HighPerformance.Test/Benchmark/MathematicsBenchmark.cs b/Misaki.HighPerformance.Test/Benchmark/MathematicsBenchmark.cs index 7b81166..292bb20 100644 --- a/Misaki.HighPerformance.Test/Benchmark/MathematicsBenchmark.cs +++ b/Misaki.HighPerformance.Test/Benchmark/MathematicsBenchmark.cs @@ -17,6 +17,9 @@ public class MathematicsBenchmark private float4 _fa = new float4(1, 2, 1, 2); private float4 _fb = new float4(3, 4, 3, 4); + private Vector64 _va64 = Vector64.Create(1f, 2f); + private Vector64 _vb64 = Vector64.Create(3f, 4f); + [Benchmark] public Vector4 VectorAdd() { @@ -28,7 +31,7 @@ public class MathematicsBenchmark return _va; } - [Benchmark] + //[Benchmark] public float4 floatAdd() { for (var i = 0; i < 10; i++) @@ -38,6 +41,17 @@ public class MathematicsBenchmark return _fa; } + + [Benchmark] + public Vector64 Vector64Add() + { + for (var i = 0; i < 10; i++) + { + _va64 += _vb64; + } + + return _va64; + } #endif #if FMA_BENCHMARK diff --git a/Misaki.HighPerformance.Test/Jobs/NoiseJobVector.cs b/Misaki.HighPerformance.Test/Jobs/NoiseJobVector.cs index 2c35491..aec6c62 100644 --- a/Misaki.HighPerformance.Test/Jobs/NoiseJobVector.cs +++ b/Misaki.HighPerformance.Test/Jobs/NoiseJobVector.cs @@ -1,4 +1,5 @@ using Misaki.HighPerformance.Jobs; +using Misaki.HighPerformance.LowLevel.Utilities; using Misaki.HighPerformance.Mathematics; using Misaki.HighPerformance.Mathematics.SPMD; using System.Numerics; @@ -60,8 +61,7 @@ internal unsafe struct NoiseJobMath : IJobParallelFor private static float2 GradientNoiseDirect(float2 uv) { - uv.x %= 289; - uv.y %= 289; + uv %= 289; var x = (34 * uv.x + 1) * uv.x % 289 + uv.y; x = (34 * x + 1) * x % 289; x = math.frac(x / 41) * 2 - 1; @@ -261,7 +261,7 @@ internal unsafe struct NoiseJobMathSPMD : IJobSPMD var uvX = (indices % w) / w; var uvY = TLane.Floor(indices / w) / h; - + var result = Noise(uvX, uvY); result.Store(buffers + baseIndex); } diff --git a/Misaki.HighPerformance.Test/Program.cs b/Misaki.HighPerformance.Test/Program.cs index f9243ce..eb151e4 100644 --- a/Misaki.HighPerformance.Test/Program.cs +++ b/Misaki.HighPerformance.Test/Program.cs @@ -1,2 +1 @@ -BenchmarkDotNet.Running.BenchmarkRunner.Run(); -return; +BenchmarkDotNet.Running.BenchmarkRunner.Run(); \ No newline at end of file diff --git a/Misaki.HighPerformance.Test/UnitTest/Jobs/CompressStoreTest.cs b/Misaki.HighPerformance.Test/UnitTest/Jobs/CompressStoreTest.cs index 13436a7..f429a51 100644 --- a/Misaki.HighPerformance.Test/UnitTest/Jobs/CompressStoreTest.cs +++ b/Misaki.HighPerformance.Test/UnitTest/Jobs/CompressStoreTest.cs @@ -39,7 +39,7 @@ public static class CompressStoreTest private unsafe static void TestPattern_Double(double[] input, bool[] keepPattern) { // 1. Setup Input Vector - // Handle case where Vector is smaller than 8 (e.g. 2 or 4) + // Handle case where Vector is smaller than 8 (e.g. 2 or 4) var vecSize = Vector.Count; var safeInput = new double[vecSize]; var safeMaskVal = new double[vecSize]; diff --git a/Misaki.HighPerformance.Test/UnitTest/Jobs/SPMDTest.cs b/Misaki.HighPerformance.Test/UnitTest/Jobs/SPMDTest.cs new file mode 100644 index 0000000..8a301ee --- /dev/null +++ b/Misaki.HighPerformance.Test/UnitTest/Jobs/SPMDTest.cs @@ -0,0 +1,60 @@ +using Misaki.HighPerformance.Mathematics; +using Misaki.HighPerformance.Mathematics.SPMD; +using System.Runtime.InteropServices; + +namespace Misaki.HighPerformance.Test.UnitTest.Jobs; + +internal unsafe struct DotProductJob : IJobSPMD +{ + public float3* arrayA; // source array 1 + public float3* arrayB; // source array 2 + public float* results; // output array (dot products) + + public readonly void Execute(int baseIndex, int threadIndex) + where TLane : ISPMD + { + var vecA = MathV.LoadVector3((float*)(arrayA + baseIndex)); + var vecB = MathV.LoadVector3((float*)(arrayB + baseIndex)); + + var dotResult = MathV.Dot(vecA, vecB); + dotResult.Store(results + baseIndex); + } +} + +[TestClass] +public class SPMDTest +{ + [TestMethod] + public unsafe void TestSPMDVectorDot() + { + const int count = 1000; + + var arrayA = (float3*)NativeMemory.Alloc((nuint)(sizeof(float3) * count)); + var arrayB = (float3*)NativeMemory.Alloc((nuint)(sizeof(float3) * count)); + var results = (float*)NativeMemory.Alloc(sizeof(float) * count); + + for (var i = 0; i < count; i++) + { + arrayA[i] = new float3(i, i + 1, i + 2); + arrayB[i] = new float3(1, 2, 3); + } + + var job = new DotProductJob + { + arrayA = arrayA, + arrayB = arrayB, + results = results + }; + + job.Run(count, -1); + + // Verify first result: dot([0,1,2], [1,2,3]) = 0*1 + 1*2 + 2*3 = 8 + Assert.AreEqual(8.0f, results[0], 0.001f); + // Verify last result: dot([999,1000,1001], [1,2,3]) = 999*1 + 1000*2 + 1001*3 = 6002 + Assert.AreEqual(6002.0f, results[count - 1], 0.001f); + + NativeMemory.Free(arrayA); + NativeMemory.Free(arrayB); + NativeMemory.Free(results); + } +} \ No newline at end of file diff --git a/Misaki.HighPerformance/Union.cs b/Misaki.HighPerformance/Union.cs deleted file mode 100644 index cb4ff2b..0000000 --- a/Misaki.HighPerformance/Union.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.Runtime.InteropServices; - -namespace Misaki.HighPerformance; - -[StructLayout(LayoutKind.Explicit)] -public struct Union - where T0 : unmanaged - where T1 : unmanaged -{ - [FieldOffset(0)] - public T0 v0; - - [FieldOffset(0)] - public T1 v1; -} \ No newline at end of file