Add Vector3<TLane, TNumber>

This commit is contained in:
2026-02-13 12:51:22 +09:00
parent f9cb909841
commit 75d33d0763
14 changed files with 1376 additions and 705 deletions

View File

@@ -128,7 +128,7 @@ namespace Misaki.HighPerformance.Mathematics.CodeGen.Generators
get get
{{ {{
RangeCheck(index); 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);
}} }}
}}"); }}");

View File

@@ -38,6 +38,20 @@ internal struct SPMDJobWrapper<T, TNumber> : IJobParallelFor
} }
} }
internal struct SPMDScalerJobWrapper<T, TNumber> : IJobParallelFor
where T : unmanaged, IJobSPMD<TNumber>
where TNumber : unmanaged, INumber<TNumber>, IMinMaxValue<TNumber>, IBitwiseOperators<TNumber, TNumber, TNumber>
{
public T innerJob;
public int totalCount;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Execute(int loopIndex, int threadIndex)
{
innerJob.Execute<ScalarLane<TNumber>>(loopIndex, threadIndex);
}
}
public static class IJobParallelForSPMDExtensions public static class IJobParallelForSPMDExtensions
{ {
public static void Run<T, TNumber>(this ref T job, int totalCount, int threadIndex) public static void Run<T, TNumber>(this ref T job, int totalCount, int threadIndex)
@@ -68,13 +82,26 @@ public static class IJobParallelForSPMDExtensions
where T : unmanaged, IJobSPMD<TNumber> where T : unmanaged, IJobSPMD<TNumber>
where TNumber : unmanaged, INumber<TNumber>, IMinMaxValue<TNumber>, IBitwiseOperators<TNumber, TNumber, TNumber> where TNumber : unmanaged, INumber<TNumber>, IMinMaxValue<TNumber>, IBitwiseOperators<TNumber, TNumber, TNumber>
{ {
var warper = new SPMDJobWrapper<T, TNumber> if (WideLane.IsSupported)
{ {
innerJob = job, var warper = new SPMDJobWrapper<T, TNumber>
totalCount = totalCount, {
}; innerJob = job,
totalCount = totalCount,
};
var iterations = (totalCount + WideLane<TNumber>.LaneWidth - 1) / WideLane<TNumber>.LaneWidth; var iterations = (totalCount + WideLane<TNumber>.LaneWidth - 1) / WideLane<TNumber>.LaneWidth;
return jobScheduler.ScheduleParallel(ref warper, iterations, batchSize, threadIndex, dependency); return jobScheduler.ScheduleParallel(ref warper, iterations, batchSize, threadIndex, dependency);
}
else
{
var warper = new SPMDScalerJobWrapper<T, TNumber>
{
innerJob = job,
totalCount = totalCount,
};
return jobScheduler.ScheduleParallel(ref warper, totalCount, batchSize, threadIndex, dependency);
}
} }
} }

View File

@@ -1,6 +1,4 @@
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices;
using static System.Runtime.InteropServices.JavaScript.JSType;
namespace Misaki.HighPerformance.Mathematics.SPMD; namespace Misaki.HighPerformance.Mathematics.SPMD;
@@ -18,12 +16,20 @@ public interface ISPMD
} }
} }
// TODO:
// - ReduceAdd
// - ReduceMin
// - ReduceMax
// - LeadingZeroCount
// - TrailingZeroCount
// - PopCount
/// <summary> /// <summary>
/// Represents a single-lane or multi-lane (vectorized) SPMD value and the operations supported on it. /// Represents a single-lane or multi-lane (vectorized) SPMD value and the operations supported on it.
/// </summary> /// </summary>
/// <typeparam name="TSelf">The concrete SPMD lane type implementing this interface.</typeparam> /// <typeparam name="TSelf">The concrete SPMD lane type implementing this interface.</typeparam>
/// <typeparam name="TNumber">The underlying numeric element type.</typeparam> /// <typeparam name="TNumber">The underlying numeric element type.</typeparam>
public interface ISPMD<TSelf, TNumber> : ISPMD public interface ISPMD<TSelf, TNumber> : ISPMD, IEquatable<TSelf>
where TSelf : ISPMD<TSelf, TNumber> where TSelf : ISPMD<TSelf, TNumber>
where TNumber : unmanaged, INumber<TNumber>, IMinMaxValue<TNumber>, IBitwiseOperators<TNumber, TNumber, TNumber> where TNumber : unmanaged, INumber<TNumber>, IMinMaxValue<TNumber>, IBitwiseOperators<TNumber, TNumber, TNumber>
{ {
@@ -159,13 +165,6 @@ public interface ISPMD<TSelf, TNumber> : ISPMD
/// <returns>The lane-wise sum.</returns> /// <returns>The lane-wise sum.</returns>
static abstract TSelf operator +(TSelf a, TSelf b); static abstract TSelf operator +(TSelf a, TSelf b);
/// <summary> /// <summary>
/// Adds a lane value and a scalar element-wise.
/// </summary>
/// <param name="a">The lane value.</param>
/// <param name="b">The scalar value.</param>
/// <returns>The lane value with the scalar added to each element.</returns>
static abstract TSelf operator +(TSelf a, TNumber b);
/// <summary>
/// Subtracts two lane values element-wise. /// Subtracts two lane values element-wise.
/// </summary> /// </summary>
/// <param name="a">The first lane value.</param> /// <param name="a">The first lane value.</param>
@@ -173,13 +172,6 @@ public interface ISPMD<TSelf, TNumber> : ISPMD
/// <returns>The lane-wise difference.</returns> /// <returns>The lane-wise difference.</returns>
static abstract TSelf operator -(TSelf a, TSelf b); static abstract TSelf operator -(TSelf a, TSelf b);
/// <summary> /// <summary>
/// Subtracts a scalar from a lane value element-wise.
/// </summary>
/// <param name="a">The lane value.</param>
/// <param name="b">The scalar value.</param>
/// <returns>The lane value with the scalar subtracted from each element.</returns>
static abstract TSelf operator -(TSelf a, TNumber b);
/// <summary>
/// Multiplies two lane values element-wise. /// Multiplies two lane values element-wise.
/// </summary> /// </summary>
/// <param name="a">The first lane value.</param> /// <param name="a">The first lane value.</param>
@@ -187,13 +179,6 @@ public interface ISPMD<TSelf, TNumber> : ISPMD
/// <returns>The lane-wise product.</returns> /// <returns>The lane-wise product.</returns>
static abstract TSelf operator *(TSelf a, TSelf b); static abstract TSelf operator *(TSelf a, TSelf b);
/// <summary> /// <summary>
/// Multiplies a lane value by a scalar element-wise.
/// </summary>
/// <param name="a">The lane value.</param>
/// <param name="b">The scalar value.</param>
/// <returns>The lane value scaled by the scalar.</returns>
static abstract TSelf operator *(TSelf a, TNumber b);
/// <summary>
/// Divides two lane values element-wise. /// Divides two lane values element-wise.
/// </summary> /// </summary>
/// <param name="a">The first lane value.</param> /// <param name="a">The first lane value.</param>
@@ -201,26 +186,12 @@ public interface ISPMD<TSelf, TNumber> : ISPMD
/// <returns>The lane-wise quotient.</returns> /// <returns>The lane-wise quotient.</returns>
static abstract TSelf operator /(TSelf a, TSelf b); static abstract TSelf operator /(TSelf a, TSelf b);
/// <summary> /// <summary>
/// Divides a lane value by a scalar element-wise.
/// </summary>
/// <param name="a">The lane value.</param>
/// <param name="b">The scalar value.</param>
/// <returns>The lane value divided by the scalar.</returns>
static abstract TSelf operator /(TSelf a, TNumber b);
/// <summary>
/// Computes the modulus of two lane values element-wise. /// Computes the modulus of two lane values element-wise.
/// </summary> /// </summary>
/// <param name="a">The first lane value.</param> /// <param name="a">The first lane value.</param>
/// <param name="b">The second lane value.</param> /// <param name="b">The second lane value.</param>
/// <returns>The lane-wise modulus.</returns> /// <returns>The lane-wise modulus.</returns>
static abstract TSelf operator %(TSelf a, TSelf b); static abstract TSelf operator %(TSelf a, TSelf b);
/// <summary>
/// Computes the modulus of a lane value and a scalar element-wise.
/// </summary>
/// <param name="a">The lane value.</param>
/// <param name="b">The scalar value.</param>
/// <returns>The lane value modulus scalar.</returns>
static abstract TSelf operator %(TSelf a, TNumber b);
/// <summary> /// <summary>
/// Negates the lane value element-wise. /// Negates the lane value element-wise.
@@ -237,13 +208,6 @@ public interface ISPMD<TSelf, TNumber> : ISPMD
/// <returns>The result of the bitwise AND.</returns> /// <returns>The result of the bitwise AND.</returns>
static abstract TSelf operator &(TSelf a, TSelf b); static abstract TSelf operator &(TSelf a, TSelf b);
/// <summary> /// <summary>
/// Computes the bitwise AND of a lane value and a scalar element-wise.
/// </summary>
/// <param name="a">The lane value.</param>
/// <param name="b">The scalar value.</param>
/// <returns>The result of the bitwise AND.</returns>
static abstract TSelf operator &(TSelf a, TNumber b);
/// <summary>
/// Computes the bitwise OR of two lane values element-wise. /// Computes the bitwise OR of two lane values element-wise.
/// </summary> /// </summary>
/// <param name="a">The first lane value.</param> /// <param name="a">The first lane value.</param>
@@ -251,13 +215,6 @@ public interface ISPMD<TSelf, TNumber> : ISPMD
/// <returns>The result of the bitwise OR.</returns> /// <returns>The result of the bitwise OR.</returns>
static abstract TSelf operator |(TSelf a, TSelf b); static abstract TSelf operator |(TSelf a, TSelf b);
/// <summary> /// <summary>
/// Computes the bitwise OR of a lane value and a scalar element-wise.
/// </summary>
/// <param name="a">The lane value.</param>
/// <param name="b">The scalar value.</param>
/// <returns>The result of the bitwise OR.</returns>
static abstract TSelf operator |(TSelf a, TNumber b);
/// <summary>
/// Computes the bitwise XOR of two lane values element-wise. /// Computes the bitwise XOR of two lane values element-wise.
/// </summary> /// </summary>
/// <param name="a">The first lane value.</param> /// <param name="a">The first lane value.</param>
@@ -265,18 +222,27 @@ public interface ISPMD<TSelf, TNumber> : ISPMD
/// <returns>The result of the bitwise XOR.</returns> /// <returns>The result of the bitwise XOR.</returns>
static abstract TSelf operator ^(TSelf a, TSelf b); static abstract TSelf operator ^(TSelf a, TSelf b);
/// <summary> /// <summary>
/// Computes the bitwise XOR of a lane value and a scalar element-wise.
/// </summary>
/// <param name="a">The lane value.</param>
/// <param name="b">The scalar value.</param>
/// <returns>The result of the bitwise XOR.</returns>
static abstract TSelf operator ^(TSelf a, TNumber b);
/// <summary>
/// Computes the bitwise NOT of a lane value element-wise. /// Computes the bitwise NOT of a lane value element-wise.
/// </summary> /// </summary>
/// <param name="a">The lane value.</param> /// <param name="a">The lane value.</param>
/// <returns>The bitwise complement of the lane value.</returns> /// <returns>The bitwise complement of the lane value.</returns>
static abstract TSelf operator ~(TSelf a); static abstract TSelf operator ~(TSelf a);
/// <summary>
/// Determines whether two instances of the type are equal component-wise.
/// </summary>
/// <param name="a">The first value to compare.</param>
/// <param name="b">The second value to compare.</param>
/// <returns>All bits set where the elements are equal; otherwise, all bits cleared.</returns>
static abstract TSelf operator ==(TSelf a, TSelf b);
/// <summary>
/// Determines whether two instances of the type are not equal component-wise.
/// </summary>
/// <param name="a">The first value to compare.</param>
/// <param name="b">The second value to compare.</param>
/// <returns>All bits set where the elements are not equal; otherwise, all bits cleared.</returns>
static abstract TSelf operator !=(TSelf a, TSelf b);
static abstract implicit operator TSelf(TNumber value);
/// <summary> /// <summary>
/// Computes the absolute value of the lane value element-wise. /// Computes the absolute value of the lane value element-wise.

View File

@@ -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<TLane, TNumber> LoadVector3<TLane, TNumber>(TNumber* pSrc)
where TLane : ISPMD<TLane, TNumber>
where TNumber : unmanaged, INumber<TNumber>, IMinMaxValue<TNumber>, IBitwiseOperators<TNumber, TNumber, TNumber>
{
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<TLane, TNumber>
{
x = TLane.Load(x),
y = TLane.Load(y),
z = TLane.Load(z)
};
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<TLane, TNumber> Load<TLane, TNumber>(ref TNumber src)
where TLane : ISPMD<TLane, TNumber>
where TNumber : unmanaged, INumber<TNumber>, IMinMaxValue<TNumber>, IBitwiseOperators<TNumber, TNumber, TNumber>
{
return LoadVector3<TLane, TNumber>((TNumber*)Unsafe.AsPointer(ref src));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<TLane, TNumber> Load<TLane, TNumber>(TNumber* pX, TNumber* pY, TNumber* pZ)
where TLane : ISPMD<TLane, TNumber>
where TNumber : unmanaged, INumber<TNumber>, IMinMaxValue<TNumber>, IBitwiseOperators<TNumber, TNumber, TNumber>
{
return new Vector3<TLane, TNumber>
{
x = TLane.Load(pX),
y = TLane.Load(pY),
z = TLane.Load(pZ)
};
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<TLane, TNumber> Load<TLane, TNumber>(ref TNumber x, ref TNumber y, ref TNumber z)
where TLane : ISPMD<TLane, TNumber>
where TNumber : unmanaged, INumber<TNumber>, IMinMaxValue<TNumber>, IBitwiseOperators<TNumber, TNumber, TNumber>
{
return new Vector3<TLane, TNumber>
{
x = TLane.Load(ref x),
y = TLane.Load(ref y),
z = TLane.Load(ref z)
};
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<TLane, TNumber> Abs<TLane, TNumber>(in Vector3<TLane, TNumber> vector)
where TLane : ISPMD<TLane, TNumber>
where TNumber : unmanaged, INumber<TNumber>, IMinMaxValue<TNumber>, IBitwiseOperators<TNumber, TNumber, TNumber>
{
return new Vector3<TLane, TNumber>
{
x = TLane.Abs(vector.x),
y = TLane.Abs(vector.y),
z = TLane.Abs(vector.z),
};
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TLane Dot<TLane, TNumber>(in Vector3<TLane, TNumber> a, in Vector3<TLane, TNumber> b)
where TLane : ISPMD<TLane, TNumber>
where TNumber : unmanaged, INumber<TNumber>, IMinMaxValue<TNumber>, IBitwiseOperators<TNumber, TNumber, TNumber>
{
return a.x * b.x + a.y * b.y + a.z * b.z;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TLane LengthSquared<TLane, TNumber>(in Vector3<TLane, TNumber> vector)
where TLane : ISPMD<TLane, TNumber>
where TNumber : unmanaged, INumber<TNumber>, IMinMaxValue<TNumber>, IBitwiseOperators<TNumber, TNumber, TNumber>
{
return Dot(vector, vector);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<TLane, TNumber> Sqrt<TLane, TNumber>(in Vector3<TLane, TNumber> vector)
where TLane : ISPMD<TLane, TNumber>
where TNumber : unmanaged, INumber<TNumber>, IMinMaxValue<TNumber>, IBitwiseOperators<TNumber, TNumber, TNumber>
{
return new Vector3<TLane, TNumber>
{
x = TLane.Sqrt(vector.x),
y = TLane.Sqrt(vector.y),
z = TLane.Sqrt(vector.z),
};
}
public static Vector3<TLane, TNumber> Rsqrt<TLane, TNumber>(in Vector3<TLane, TNumber> vector)
where TLane : ISPMD<TLane, TNumber>
where TNumber : unmanaged, INumber<TNumber>, IMinMaxValue<TNumber>, IBitwiseOperators<TNumber, TNumber, TNumber>
{
return new Vector3<TLane, TNumber>
{
x = TLane.Rsqrt(vector.x),
y = TLane.Rsqrt(vector.y),
z = TLane.Rsqrt(vector.z),
};
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<TLane, TNumber> Normalize<TLane, TNumber>(in Vector3<TLane, TNumber> vector)
where TLane : ISPMD<TLane, TNumber>
where TNumber : unmanaged, INumber<TNumber>, IMinMaxValue<TNumber>, IBitwiseOperators<TNumber, TNumber, TNumber>
{
return vector * TLane.Rsqrt(Dot(vector, vector));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<TLane, TNumber> Cross<TLane, TNumber>(in Vector3<TLane, TNumber> a, in Vector3<TLane, TNumber> b)
where TLane : ISPMD<TLane, TNumber>
where TNumber : unmanaged, INumber<TNumber>, IMinMaxValue<TNumber>, IBitwiseOperators<TNumber, TNumber, TNumber>
{
return new Vector3<TLane, TNumber>
{
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<TLane, TNumber> Reflect<TLane, TNumber>(in Vector3<TLane, TNumber> incident, in Vector3<TLane, TNumber> normal)
where TLane : ISPMD<TLane, TNumber>
where TNumber : unmanaged, INumber<TNumber>, IMinMaxValue<TNumber>, IBitwiseOperators<TNumber, TNumber, TNumber>
{
var dot = Dot(incident, normal);
return incident - normal * (dot + dot);
}
}

View File

@@ -5,10 +5,10 @@ using System.Runtime.InteropServices;
namespace Misaki.HighPerformance.Mathematics.SPMD; namespace Misaki.HighPerformance.Mathematics.SPMD;
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
public readonly unsafe struct ScalarLane<T> : ISPMD<ScalarLane<T>, T> public readonly unsafe struct ScalarLane<TNumber> : ISPMD<ScalarLane<TNumber>, TNumber>
where T : unmanaged, INumber<T>, IMinMaxValue<T>, IBitwiseOperators<T, T, T> where TNumber : unmanaged, INumber<TNumber>, IMinMaxValue<TNumber>, IBitwiseOperators<TNumber, TNumber, TNumber>
{ {
public readonly T value; public readonly TNumber value;
public static int LaneWidth public static int LaneWidth
{ {
@@ -16,77 +16,99 @@ public readonly unsafe struct ScalarLane<T> : ISPMD<ScalarLane<T>, T>
get => 1; get => 1;
} }
public static ScalarLane<T> Zero public static ScalarLane<TNumber> Zero
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
get => new(T.Zero); get => new(TNumber.Zero);
} }
public static ScalarLane<T> One public static ScalarLane<TNumber> One
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
get => new(T.One); get => new(TNumber.One);
} }
public static ScalarLane<T> MinValue public static ScalarLane<TNumber> MinValue
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
get => new(T.MinValue); get => new(TNumber.MinValue);
} }
public static ScalarLane<T> MaxValue public static ScalarLane<TNumber> MaxValue
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining)] [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)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
get => value; get => value;
} }
public ScalarLane(T value) public ScalarLane(TNumber value)
{ {
this.value = value; this.value = value;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<T> Create(T value) => new(value); public static ScalarLane<TNumber> Create(TNumber value)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<T> Create(params ReadOnlySpan<T> values) => new(values[0]);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<T> Create(Vector<T> value) => new(value[0]);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<T> Sequence(T start, T step) => new(start);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<T> Load(ref T value) => new(value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<T> 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<T> mask, ref T destination)
{ {
return CompressStore(mask, (T*)Unsafe.AsPointer(in destination)); return new(value);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public int CompressStore(ScalarLane<T> mask, T* pDestination) public static ScalarLane<TNumber> Create(params ReadOnlySpan<TNumber> values)
{ {
if (mask.value != T.Zero) return new(values[0]);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<TNumber> Create(Vector<TNumber> value)
{
return new(value[0]);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<TNumber> Sequence(TNumber start, TNumber step)
{
return new(start);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<TNumber> Load(ref TNumber value)
{
return new(value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<TNumber> 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<TNumber> mask, ref TNumber destination)
{
return CompressStore(mask, (TNumber*)Unsafe.AsPointer(in destination));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int CompressStore(ScalarLane<TNumber> mask, TNumber* pDestination)
{
if (mask.value != TNumber.Zero)
{ {
*pDestination = value; *pDestination = value;
return 1; return 1;
@@ -96,299 +118,358 @@ public readonly unsafe struct ScalarLane<T> : ISPMD<ScalarLane<T>, T>
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector<T> AsVector() => Vector.Create(value); public readonly Vector<TNumber> AsVector()
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<T> operator +(ScalarLane<T> a, ScalarLane<T> b) => new(a.value + b.value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<T> operator +(ScalarLane<T> a, T b) => new(a.value + b);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<T> operator -(ScalarLane<T> a, ScalarLane<T> b) => new(a.value - b.value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<T> operator -(ScalarLane<T> a, T b) => new(a.value - b);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<T> operator *(ScalarLane<T> a, ScalarLane<T> b) => new(a.value * b.value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<T> operator *(ScalarLane<T> a, T b) => new(a.value * b);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<T> operator /(ScalarLane<T> a, ScalarLane<T> b) => new(a.value / b.value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<T> operator /(ScalarLane<T> a, T b) => new(a.value / b);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<T> operator %(ScalarLane<T> a, ScalarLane<T> b) => new(a.value % b.value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<T> operator %(ScalarLane<T> a, T b) => new(a.value % b);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<T> operator -(ScalarLane<T> a) => new(-a.value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<T> operator &(ScalarLane<T> a, ScalarLane<T> b) => new(a.value & b.value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<T> operator &(ScalarLane<T> a, T b) => new(a.value & b);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<T> operator |(ScalarLane<T> a, ScalarLane<T> b) => new(a.value | b.value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<T> operator |(ScalarLane<T> a, T b) => new(a.value | b);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<T> operator ^(ScalarLane<T> a, ScalarLane<T> b) => new(a.value ^ b.value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<T> operator ^(ScalarLane<T> a, T b) => new(a.value ^ b);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<T> operator ~(ScalarLane<T> a) => new(~a.value);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<T> Abs(ScalarLane<T> value) => new(T.Abs(value.value));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<T> Floor(ScalarLane<T> value)
{ {
// Note: INumber<T> does not provide Floor method, so we need to handle float and double specifically. return Vector.Create(value);
// This is acceptable for performance because JIT generates specialized code for each T as long as they are struct. }
// Which mean for ScalarLane<float>, typeof(T) == typeof(float) is always true and jit will optimize away the other branches.
if (typeof(T) == typeof(float)) [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<TNumber> operator +(ScalarLane<TNumber> a, ScalarLane<TNumber> b)
{
return new(a.value + b.value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<TNumber> operator -(ScalarLane<TNumber> a, ScalarLane<TNumber> b)
{
return new(a.value - b.value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<TNumber> operator *(ScalarLane<TNumber> a, ScalarLane<TNumber> b)
{
return new(a.value * b.value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<TNumber> operator /(ScalarLane<TNumber> a, ScalarLane<TNumber> b)
{
return new(a.value / b.value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<TNumber> operator %(ScalarLane<TNumber> a, ScalarLane<TNumber> b)
{
return new(a.value % b.value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<TNumber> operator -(ScalarLane<TNumber> a)
{
return new(-a.value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<TNumber> operator &(ScalarLane<TNumber> a, ScalarLane<TNumber> b)
{
return new(a.value & b.value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<TNumber> operator |(ScalarLane<TNumber> a, ScalarLane<TNumber> b)
{
return new(a.value | b.value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<TNumber> operator ^(ScalarLane<TNumber> a, ScalarLane<TNumber> b)
{
return new(a.value ^ b.value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<TNumber> operator ~(ScalarLane<TNumber> a)
{
return new(~a.value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<TNumber> operator ==(ScalarLane<TNumber> a, ScalarLane<TNumber> b)
{
return new(a.value == b.value ? ~TNumber.Zero : TNumber.Zero);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<TNumber> operator !=(ScalarLane<TNumber> a, ScalarLane<TNumber> b)
{
return new(a.value != b.value ? ~TNumber.Zero : TNumber.Zero);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator ScalarLane<TNumber>(TNumber value)
{
return new(value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<TNumber> Abs(ScalarLane<TNumber> value)
{
return new(TNumber.Abs(value.value));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<TNumber> Floor(ScalarLane<TNumber> value)
{
// Note: INumber<TLane> 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<float>, typeof(TLane) == typeof(float) is always true and jit will optimize away the other branches.
if (typeof(TNumber) == typeof(float))
{ {
var f = Unsafe.As<ScalarLane<T>, float>(ref value); var f = Unsafe.As<ScalarLane<TNumber>, float>(ref value);
var result = MathF.Floor(f); var result = MathF.Floor(f);
return Unsafe.As<float, ScalarLane<T>>(ref result); return Unsafe.As<float, ScalarLane<TNumber>>(ref result);
} }
else if (typeof(T) == typeof(double)) else if (typeof(TNumber) == typeof(double))
{ {
var d = Unsafe.As<ScalarLane<T>, double>(ref value); var d = Unsafe.As<ScalarLane<TNumber>, double>(ref value);
var result = Math.Floor(d); var result = Math.Floor(d);
return Unsafe.As<double, ScalarLane<T>>(ref result); return Unsafe.As<double, ScalarLane<TNumber>>(ref result);
} }
return value; return value;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<T> Frac(ScalarLane<T> value) => new(value.value - T.CreateTruncating(value.value)); public static ScalarLane<TNumber> Frac(ScalarLane<TNumber> value)
{
return new(value.value - TNumber.CreateTruncating(value.value));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<T> Sqrt(ScalarLane<T> value) public static ScalarLane<TNumber> Sqrt(ScalarLane<TNumber> value)
{ {
if (typeof(T) == typeof(float)) if (typeof(TNumber) == typeof(float))
{ {
var f = Unsafe.As<ScalarLane<T>, float>(ref value); var f = Unsafe.As<ScalarLane<TNumber>, float>(ref value);
var result = MathF.Sqrt(f); var result = MathF.Sqrt(f);
return Unsafe.As<float, ScalarLane<T>>(ref result); return Unsafe.As<float, ScalarLane<TNumber>>(ref result);
} }
else if (typeof(T) == typeof(double)) else if (typeof(TNumber) == typeof(double))
{ {
var d = Unsafe.As<ScalarLane<T>, double>(ref value); var d = Unsafe.As<ScalarLane<TNumber>, double>(ref value);
var result = Math.Sqrt(d); var result = Math.Sqrt(d);
return Unsafe.As<double, ScalarLane<T>>(ref result); return Unsafe.As<double, ScalarLane<TNumber>>(ref result);
} }
return value; return value;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<T> Lerp(ScalarLane<T> a, ScalarLane<T> b, ScalarLane<T> t) => new(a.value + (b.value - a.value) * t.value); public static ScalarLane<TNumber> Lerp(ScalarLane<TNumber> a, ScalarLane<TNumber> b, ScalarLane<TNumber> t)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<T> MultipleAdd(ScalarLane<T> a, ScalarLane<T> b, ScalarLane<T> c) => new(T.MultiplyAddEstimate(a.value, b.value, c.value));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<T> Min(ScalarLane<T> a, ScalarLane<T> b) => new(T.Min(a.value, b.value));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<T> Max(ScalarLane<T> a, ScalarLane<T> b) => new(T.Max(a.value, b.value));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<T> Clamp(ScalarLane<T> value, ScalarLane<T> min, ScalarLane<T> max) => new(T.Clamp(value.value, min.value, max.value));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<T> Saturate(ScalarLane<T> value) => Clamp(value, new(T.Zero), new(T.One));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<T> Sin(ScalarLane<T> value)
{ {
if (typeof(T) == typeof(float)) return new(a.value + (b.value - a.value) * t.value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<TNumber> MultipleAdd(ScalarLane<TNumber> a, ScalarLane<TNumber> b, ScalarLane<TNumber> c)
{
return new(TNumber.MultiplyAddEstimate(a.value, b.value, c.value));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<TNumber> Min(ScalarLane<TNumber> a, ScalarLane<TNumber> b)
{
return new(TNumber.Min(a.value, b.value));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<TNumber> Max(ScalarLane<TNumber> a, ScalarLane<TNumber> b)
{
return new(TNumber.Max(a.value, b.value));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<TNumber> Clamp(ScalarLane<TNumber> value, ScalarLane<TNumber> min, ScalarLane<TNumber> max)
{
return new(TNumber.Clamp(value.value, min.value, max.value));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<TNumber> Saturate(ScalarLane<TNumber> value)
{
return Clamp(value, new(TNumber.Zero), new(TNumber.One));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<TNumber> Sin(ScalarLane<TNumber> value)
{
if (typeof(TNumber) == typeof(float))
{ {
var f = Unsafe.As<ScalarLane<T>, float>(ref value); var f = Unsafe.As<ScalarLane<TNumber>, float>(ref value);
var result = MathF.Sin(f); var result = MathF.Sin(f);
return Unsafe.As<float, ScalarLane<T>>(ref result); return Unsafe.As<float, ScalarLane<TNumber>>(ref result);
} }
else if (typeof(T) == typeof(double)) else if (typeof(TNumber) == typeof(double))
{ {
var d = Unsafe.As<ScalarLane<T>, double>(ref value); var d = Unsafe.As<ScalarLane<TNumber>, double>(ref value);
var result = Math.Sin(d); var result = Math.Sin(d);
return Unsafe.As<double, ScalarLane<T>>(ref result); return Unsafe.As<double, ScalarLane<TNumber>>(ref result);
} }
return value; return value;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<T> Cos(ScalarLane<T> value) public static ScalarLane<TNumber> Cos(ScalarLane<TNumber> value)
{ {
if (typeof(T) == typeof(float)) if (typeof(TNumber) == typeof(float))
{ {
var f = Unsafe.As<ScalarLane<T>, float>(ref value); var f = Unsafe.As<ScalarLane<TNumber>, float>(ref value);
var result = MathF.Cos(f); var result = MathF.Cos(f);
return Unsafe.As<float, ScalarLane<T>>(ref result); return Unsafe.As<float, ScalarLane<TNumber>>(ref result);
} }
else if (typeof(T) == typeof(double)) else if (typeof(TNumber) == typeof(double))
{ {
var d = Unsafe.As<ScalarLane<T>, double>(ref value); var d = Unsafe.As<ScalarLane<TNumber>, double>(ref value);
var result = Math.Cos(d); var result = Math.Cos(d);
return Unsafe.As<double, ScalarLane<T>>(ref result); return Unsafe.As<double, ScalarLane<TNumber>>(ref result);
} }
return value; return value;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static (ScalarLane<T> sin, ScalarLane<T> cos) SinCos(ScalarLane<T> value) => (Sin(value), Cos(value)); public static (ScalarLane<TNumber> sin, ScalarLane<TNumber> cos) SinCos(ScalarLane<TNumber> value)
{
return (Sin(value), Cos(value));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<T> Tan(ScalarLane<T> value) public static ScalarLane<TNumber> Tan(ScalarLane<TNumber> value)
{ {
if (typeof(T) == typeof(float)) if (typeof(TNumber) == typeof(float))
{ {
var f = Unsafe.As<ScalarLane<T>, float>(ref value); var f = Unsafe.As<ScalarLane<TNumber>, float>(ref value);
var result = MathF.Tan(f); var result = MathF.Tan(f);
return Unsafe.As<float, ScalarLane<T>>(ref result); return Unsafe.As<float, ScalarLane<TNumber>>(ref result);
} }
else if (typeof(T) == typeof(double)) else if (typeof(TNumber) == typeof(double))
{ {
var d = Unsafe.As<ScalarLane<T>, double>(ref value); var d = Unsafe.As<ScalarLane<TNumber>, double>(ref value);
var result = Math.Tan(d); var result = Math.Tan(d);
return Unsafe.As<double, ScalarLane<T>>(ref result); return Unsafe.As<double, ScalarLane<TNumber>>(ref result);
} }
return value; return value;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<T> Asin(ScalarLane<T> value) public static ScalarLane<TNumber> Asin(ScalarLane<TNumber> value)
{ {
if (typeof(T) == typeof(float)) if (typeof(TNumber) == typeof(float))
{ {
var f = Unsafe.As<ScalarLane<T>, float>(ref value); var f = Unsafe.As<ScalarLane<TNumber>, float>(ref value);
var result = MathF.Asin(f); var result = MathF.Asin(f);
return Unsafe.As<float, ScalarLane<T>>(ref result); return Unsafe.As<float, ScalarLane<TNumber>>(ref result);
} }
else if (typeof(T) == typeof(double)) else if (typeof(TNumber) == typeof(double))
{ {
var d = Unsafe.As<ScalarLane<T>, double>(ref value); var d = Unsafe.As<ScalarLane<TNumber>, double>(ref value);
var result = Math.Asin(d); var result = Math.Asin(d);
return Unsafe.As<double, ScalarLane<T>>(ref result); return Unsafe.As<double, ScalarLane<TNumber>>(ref result);
} }
return value; return value;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<T> Acos(ScalarLane<T> value) public static ScalarLane<TNumber> Acos(ScalarLane<TNumber> value)
{ {
if (typeof(T) == typeof(float)) if (typeof(TNumber) == typeof(float))
{ {
var f = Unsafe.As<ScalarLane<T>, float>(ref value); var f = Unsafe.As<ScalarLane<TNumber>, float>(ref value);
var result = MathF.Acos(f); var result = MathF.Acos(f);
return Unsafe.As<float, ScalarLane<T>>(ref result); return Unsafe.As<float, ScalarLane<TNumber>>(ref result);
} }
else if (typeof(T) == typeof(double)) else if (typeof(TNumber) == typeof(double))
{ {
var d = Unsafe.As<ScalarLane<T>, double>(ref value); var d = Unsafe.As<ScalarLane<TNumber>, double>(ref value);
var result = Math.Acos(d); var result = Math.Acos(d);
return Unsafe.As<double, ScalarLane<T>>(ref result); return Unsafe.As<double, ScalarLane<TNumber>>(ref result);
} }
return value; return value;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<T> Atan(ScalarLane<T> value) public static ScalarLane<TNumber> Atan(ScalarLane<TNumber> value)
{ {
if (typeof(T) == typeof(float)) if (typeof(TNumber) == typeof(float))
{ {
var f = Unsafe.As<ScalarLane<T>, float>(ref value); var f = Unsafe.As<ScalarLane<TNumber>, float>(ref value);
var result = MathF.Atan(f); var result = MathF.Atan(f);
return Unsafe.As<float, ScalarLane<T>>(ref result); return Unsafe.As<float, ScalarLane<TNumber>>(ref result);
} }
else if (typeof(T) == typeof(double)) else if (typeof(TNumber) == typeof(double))
{ {
var d = Unsafe.As<ScalarLane<T>, double>(ref value); var d = Unsafe.As<ScalarLane<TNumber>, double>(ref value);
var result = Math.Atan(d); var result = Math.Atan(d);
return Unsafe.As<double, ScalarLane<T>>(ref result); return Unsafe.As<double, ScalarLane<TNumber>>(ref result);
} }
return value; return value;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<T> Atan2(ScalarLane<T> y, ScalarLane<T> x) public static ScalarLane<TNumber> Atan2(ScalarLane<TNumber> y, ScalarLane<TNumber> x)
{ {
if (typeof(T) == typeof(float)) if (typeof(TNumber) == typeof(float))
{ {
var fy = Unsafe.As<ScalarLane<T>, float>(ref y); var fy = Unsafe.As<ScalarLane<TNumber>, float>(ref y);
var fx = Unsafe.As<ScalarLane<T>, float>(ref x); var fx = Unsafe.As<ScalarLane<TNumber>, float>(ref x);
var result = MathF.Atan2(fy, fx); var result = MathF.Atan2(fy, fx);
return Unsafe.As<float, ScalarLane<T>>(ref result); return Unsafe.As<float, ScalarLane<TNumber>>(ref result);
} }
else if (typeof(T) == typeof(double)) else if (typeof(TNumber) == typeof(double))
{ {
var dy = Unsafe.As<ScalarLane<T>, double>(ref y); var dy = Unsafe.As<ScalarLane<TNumber>, double>(ref y);
var dx = Unsafe.As<ScalarLane<T>, double>(ref x); var dx = Unsafe.As<ScalarLane<TNumber>, double>(ref x);
var result = Math.Atan2(dy, dx); var result = Math.Atan2(dy, dx);
return Unsafe.As<double, ScalarLane<T>>(ref result); return Unsafe.As<double, ScalarLane<TNumber>>(ref result);
} }
return y; return y;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<T> Pow(ScalarLane<T> x, ScalarLane<T> y) public static ScalarLane<TNumber> Pow(ScalarLane<TNumber> x, ScalarLane<TNumber> y)
{ {
if (typeof(T) == typeof(float)) if (typeof(TNumber) == typeof(float))
{ {
var fx = Unsafe.As<ScalarLane<T>, float>(ref x); var fx = Unsafe.As<ScalarLane<TNumber>, float>(ref x);
var fy = Unsafe.As<ScalarLane<T>, float>(ref y); var fy = Unsafe.As<ScalarLane<TNumber>, float>(ref y);
var result = MathF.Pow(fx, fy); var result = MathF.Pow(fx, fy);
return Unsafe.As<float, ScalarLane<T>>(ref result); return Unsafe.As<float, ScalarLane<TNumber>>(ref result);
} }
else if (typeof(T) == typeof(double)) else if (typeof(TNumber) == typeof(double))
{ {
var dx = Unsafe.As<ScalarLane<T>, double>(ref x); var dx = Unsafe.As<ScalarLane<TNumber>, double>(ref x);
var dy = Unsafe.As<ScalarLane<T>, double>(ref y); var dy = Unsafe.As<ScalarLane<TNumber>, double>(ref y);
var result = Math.Pow(dx, dy); var result = Math.Pow(dx, dy);
return Unsafe.As<double, ScalarLane<T>>(ref result); return Unsafe.As<double, ScalarLane<TNumber>>(ref result);
} }
return x; return x;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<T> Exp(ScalarLane<T> value) public static ScalarLane<TNumber> Exp(ScalarLane<TNumber> value)
{ {
if (typeof(T) == typeof(float)) if (typeof(TNumber) == typeof(float))
{ {
var f = Unsafe.As<ScalarLane<T>, float>(ref value); var f = Unsafe.As<ScalarLane<TNumber>, float>(ref value);
var result = MathF.Exp(f); var result = MathF.Exp(f);
return Unsafe.As<float, ScalarLane<T>>(ref result); return Unsafe.As<float, ScalarLane<TNumber>>(ref result);
} }
else if (typeof(T) == typeof(double)) else if (typeof(TNumber) == typeof(double))
{ {
var d = Unsafe.As<ScalarLane<T>, double>(ref value); var d = Unsafe.As<ScalarLane<TNumber>, double>(ref value);
var result = Math.Log(d); var result = Math.Log(d);
return Unsafe.As<double, ScalarLane<T>>(ref result); return Unsafe.As<double, ScalarLane<TNumber>>(ref result);
} }
return value; return value;
@@ -396,167 +477,222 @@ public readonly unsafe struct ScalarLane<T> : ISPMD<ScalarLane<T>, T>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<T> Exp2(ScalarLane<T> value) public static ScalarLane<TNumber> Exp2(ScalarLane<TNumber> value)
{ {
return Pow(new ScalarLane<T>(T.CreateChecked(2)), value); return Pow(new ScalarLane<TNumber>(TNumber.CreateChecked(2)), value);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<T> Log(ScalarLane<T> value) public static ScalarLane<TNumber> Log(ScalarLane<TNumber> value)
{ {
if (typeof(T) == typeof(float)) if (typeof(TNumber) == typeof(float))
{ {
var f = Unsafe.As<ScalarLane<T>, float>(ref value); var f = Unsafe.As<ScalarLane<TNumber>, float>(ref value);
var result = MathF.Log(f); var result = MathF.Log(f);
return Unsafe.As<float, ScalarLane<T>>(ref result); return Unsafe.As<float, ScalarLane<TNumber>>(ref result);
} }
else if (typeof(T) == typeof(double)) else if (typeof(TNumber) == typeof(double))
{ {
var d = Unsafe.As<ScalarLane<T>, double>(ref value); var d = Unsafe.As<ScalarLane<TNumber>, double>(ref value);
var result = Math.Log(d); var result = Math.Log(d);
return Unsafe.As<double, ScalarLane<T>>(ref result); return Unsafe.As<double, ScalarLane<TNumber>>(ref result);
} }
return value; return value;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<T> Log2(ScalarLane<T> value) public static ScalarLane<TNumber> Log2(ScalarLane<TNumber> value)
{ {
if (typeof(T) == typeof(float)) if (typeof(TNumber) == typeof(float))
{ {
var f = Unsafe.As<ScalarLane<T>, float>(ref value); var f = Unsafe.As<ScalarLane<TNumber>, float>(ref value);
var result = MathF.Log2(f); var result = MathF.Log2(f);
return Unsafe.As<float, ScalarLane<T>>(ref result); return Unsafe.As<float, ScalarLane<TNumber>>(ref result);
} }
else if (typeof(T) == typeof(double)) else if (typeof(TNumber) == typeof(double))
{ {
var d = Unsafe.As<ScalarLane<T>, double>(ref value); var d = Unsafe.As<ScalarLane<TNumber>, double>(ref value);
var result = Math.Log2(d); var result = Math.Log2(d);
return Unsafe.As<double, ScalarLane<T>>(ref result); return Unsafe.As<double, ScalarLane<TNumber>>(ref result);
} }
return value; return value;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<T> Ceil(ScalarLane<T> value) public static ScalarLane<TNumber> Ceil(ScalarLane<TNumber> value)
{ {
if (typeof(T) == typeof(float)) if (typeof(TNumber) == typeof(float))
{ {
var f = Unsafe.As<ScalarLane<T>, float>(ref value); var f = Unsafe.As<ScalarLane<TNumber>, float>(ref value);
var result = MathF.Ceiling(f); var result = MathF.Ceiling(f);
return Unsafe.As<float, ScalarLane<T>>(ref result); return Unsafe.As<float, ScalarLane<TNumber>>(ref result);
} }
else if (typeof(T) == typeof(double)) else if (typeof(TNumber) == typeof(double))
{ {
var d = Unsafe.As<ScalarLane<T>, double>(ref value); var d = Unsafe.As<ScalarLane<TNumber>, double>(ref value);
var result = Math.Ceiling(d); var result = Math.Ceiling(d);
return Unsafe.As<double, ScalarLane<T>>(ref result); return Unsafe.As<double, ScalarLane<TNumber>>(ref result);
} }
else if (typeof(T) == typeof(decimal)) else if (typeof(TNumber) == typeof(decimal))
{ {
var d = Unsafe.As<ScalarLane<T>, decimal>(ref value); var d = Unsafe.As<ScalarLane<TNumber>, decimal>(ref value);
var result = Math.Ceiling(d); var result = Math.Ceiling(d);
return Unsafe.As<decimal, ScalarLane<T>>(ref result); return Unsafe.As<decimal, ScalarLane<TNumber>>(ref result);
} }
return value; return value;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<T> Round(ScalarLane<T> value) public static ScalarLane<TNumber> Round(ScalarLane<TNumber> value)
{ {
if (typeof(T) == typeof(float)) if (typeof(TNumber) == typeof(float))
{ {
var f = Unsafe.As<ScalarLane<T>, float>(ref value); var f = Unsafe.As<ScalarLane<TNumber>, float>(ref value);
var result = MathF.Round(f); var result = MathF.Round(f);
return Unsafe.As<float, ScalarLane<T>>(ref result); return Unsafe.As<float, ScalarLane<TNumber>>(ref result);
} }
else if (typeof(T) == typeof(double)) else if (typeof(TNumber) == typeof(double))
{ {
var d = Unsafe.As<ScalarLane<T>, double>(ref value); var d = Unsafe.As<ScalarLane<TNumber>, double>(ref value);
var result = Math.Round(d); var result = Math.Round(d);
return Unsafe.As<double, ScalarLane<T>>(ref result); return Unsafe.As<double, ScalarLane<TNumber>>(ref result);
} }
else if (typeof(T) == typeof(decimal)) else if (typeof(TNumber) == typeof(decimal))
{ {
var d = Unsafe.As<ScalarLane<T>, decimal>(ref value); var d = Unsafe.As<ScalarLane<TNumber>, decimal>(ref value);
var result = Math.Round(d); var result = Math.Round(d);
return Unsafe.As<decimal, ScalarLane<T>>(ref result); return Unsafe.As<decimal, ScalarLane<TNumber>>(ref result);
} }
return value; return value;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<T> Trunc(ScalarLane<T> value) public static ScalarLane<TNumber> Trunc(ScalarLane<TNumber> value)
{ {
if (typeof(T) == typeof(float)) if (typeof(TNumber) == typeof(float))
{ {
var f = Unsafe.As<ScalarLane<T>, float>(ref value); var f = Unsafe.As<ScalarLane<TNumber>, float>(ref value);
var result = MathF.Truncate(f); var result = MathF.Truncate(f);
return Unsafe.As<float, ScalarLane<T>>(ref result); return Unsafe.As<float, ScalarLane<TNumber>>(ref result);
} }
else if (typeof(T) == typeof(double)) else if (typeof(TNumber) == typeof(double))
{ {
var d = Unsafe.As<ScalarLane<T>, double>(ref value); var d = Unsafe.As<ScalarLane<TNumber>, double>(ref value);
var result = Math.Truncate(d); var result = Math.Truncate(d);
return Unsafe.As<double, ScalarLane<T>>(ref result); return Unsafe.As<double, ScalarLane<TNumber>>(ref result);
} }
else if (typeof(T) == typeof(decimal)) else if (typeof(TNumber) == typeof(decimal))
{ {
var d = Unsafe.As<ScalarLane<T>, decimal>(ref value); var d = Unsafe.As<ScalarLane<TNumber>, decimal>(ref value);
var result = Math.Truncate(d); var result = Math.Truncate(d);
return Unsafe.As<decimal, ScalarLane<T>>(ref result); return Unsafe.As<decimal, ScalarLane<TNumber>>(ref result);
} }
return value; return value;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<T> Sign(ScalarLane<T> value) => new((value.value > T.Zero) ? T.One : (value.value < T.Zero) ? ~T.Zero : T.Zero); public static ScalarLane<TNumber> Sign(ScalarLane<TNumber> value)
{
return new((value.value > TNumber.Zero) ? TNumber.One : (value.value < TNumber.Zero) ? ~TNumber.Zero : TNumber.Zero);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<T> CopySign(ScalarLane<T> magnitude, ScalarLane<T> sign) => new(T.CopySign(magnitude.value, sign.value)); public static ScalarLane<TNumber> CopySign(ScalarLane<TNumber> magnitude, ScalarLane<TNumber> sign)
{
return new(TNumber.CopySign(magnitude.value, sign.value));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<T> Rcp(ScalarLane<T> value) => new(T.One / value.value); public static ScalarLane<TNumber> Rcp(ScalarLane<TNumber> value)
{
return new(TNumber.One / value.value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<T> Rsqrt(ScalarLane<T> value) => Sqrt(Rcp(value)); public static ScalarLane<TNumber> Rsqrt(ScalarLane<TNumber> value)
{
return Sqrt(Rcp(value));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<TNumber> Select(ScalarLane<TNumber> conditionMask, ScalarLane<TNumber> ifTrue, ScalarLane<TNumber> ifFalse)
{
return new(conditionMask.value != TNumber.Zero ? ifTrue.value : ifFalse.value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<TNumber> GreaterThan(ScalarLane<TNumber> a, ScalarLane<TNumber> b)
{
return new(a.value > b.value ? ~TNumber.Zero : TNumber.Zero);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<TNumber> GreaterThanOrEqual(ScalarLane<TNumber> a, ScalarLane<TNumber> b)
{
return new(a.value >= b.value ? ~TNumber.Zero : TNumber.Zero);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<TNumber> LessThan(ScalarLane<TNumber> a, ScalarLane<TNumber> b)
{
return new(a.value < b.value ? ~TNumber.Zero : TNumber.Zero);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<TNumber> LessThanOrEqual(ScalarLane<TNumber> a, ScalarLane<TNumber> b)
{
return new(a.value <= b.value ? ~TNumber.Zero : TNumber.Zero);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<TNumber> Equal(ScalarLane<TNumber> a, ScalarLane<TNumber> b)
{
return new(a.value == b.value ? ~TNumber.Zero : TNumber.Zero);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool Any(ScalarLane<TNumber> mask)
{
return mask.value != TNumber.Zero;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool All(ScalarLane<TNumber> mask)
{
return mask.value != TNumber.Zero;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool None(ScalarLane<TNumber> mask)
{
return mask.value == TNumber.Zero;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<T> Select(ScalarLane<T> conditionMask, ScalarLane<T> ifTrue, ScalarLane<T> ifFalse) => new(conditionMask.value != T.Zero ? ifTrue.value : ifFalse.value); public bool Equals(ScalarLane<TNumber> other)
{
return value.Equals(other.value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<T> GreaterThan(ScalarLane<T> a, ScalarLane<T> b) => new(a.value > b.value ? ~T.Zero : T.Zero); public override bool Equals(object? obj)
{
return obj is ScalarLane<TNumber> other && value.Equals(other.value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<T> GreaterThanOrEqual(ScalarLane<T> a, ScalarLane<T> b) => new(a.value >= b.value ? ~T.Zero : T.Zero); public override int GetHashCode()
{
return value.GetHashCode();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<T> LessThan(ScalarLane<T> a, ScalarLane<T> b) => new(a.value < b.value ? ~T.Zero : T.Zero);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<T> LessThanOrEqual(ScalarLane<T> a, ScalarLane<T> b) => new(a.value <= b.value ? ~T.Zero : T.Zero);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ScalarLane<T> Equal(ScalarLane<T> a, ScalarLane<T> b) => new(a.value == b.value ? ~T.Zero : T.Zero);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool Any(ScalarLane<T> mask) => mask.value != T.Zero;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool All(ScalarLane<T> mask) => mask.value != T.Zero;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool None(ScalarLane<T> mask) => mask.value == T.Zero;
public override string ToString() public override string ToString()
{ {
return value.ToString() ?? string.Empty; return value.ToString() ?? string.Empty;

View File

@@ -2,7 +2,7 @@ using System.Runtime.InteropServices;
namespace Misaki.HighPerformance.Mathematics.SPMD; namespace Misaki.HighPerformance.Mathematics.SPMD;
public static unsafe class ShuffleTableGenerator internal static unsafe class ShuffleTableGenerator
{ {
public static uint* ComputeShuffleTable512_32Bit() public static uint* ComputeShuffleTable512_32Bit()
{ {

View File

@@ -0,0 +1,198 @@
using System.Diagnostics;
using System.Numerics;
using System.Runtime.CompilerServices;
namespace Misaki.HighPerformance.Mathematics.SPMD;
public unsafe struct Vector3<TLane, TNumber> : IEquatable<Vector3<TLane, TNumber>>
where TLane : ISPMD<TLane, TNumber>
where TNumber : unmanaged, INumber<TNumber>, IMinMaxValue<TNumber>, IBitwiseOperators<TNumber, TNumber, TNumber>
{
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<TLane, TNumber> operator +(in Vector3<TLane, TNumber> left, in Vector3<TLane, TNumber> right)
{
return new Vector3<TLane, TNumber>
{
x = left.x + right.x,
y = left.y + right.y,
z = left.z + right.z,
};
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<TLane, TNumber> operator +(in Vector3<TLane, TNumber> vector, TLane lane)
{
return new Vector3<TLane, TNumber>
{
x = vector.x + lane,
y = vector.y + lane,
z = vector.z + lane,
};
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<TLane, TNumber> operator -(in Vector3<TLane, TNumber> left, in Vector3<TLane, TNumber> right)
{
return new Vector3<TLane, TNumber>
{
x = left.x - right.x,
y = left.y - right.y,
z = left.z - right.z,
};
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<TLane, TNumber> operator -(in Vector3<TLane, TNumber> vector, TLane lane)
{
return new Vector3<TLane, TNumber>
{
x = vector.x - lane,
y = vector.y - lane,
z = vector.z - lane,
};
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<TLane, TNumber> operator *(in Vector3<TLane, TNumber> left, in Vector3<TLane, TNumber> right)
{
return new Vector3<TLane, TNumber>
{
x = left.x * right.x,
y = left.y * right.y,
z = left.z * right.z,
};
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<TLane, TNumber> operator *(in Vector3<TLane, TNumber> vector, TLane lane)
{
return new Vector3<TLane, TNumber>
{
x = vector.x * lane,
y = vector.y * lane,
z = vector.z * lane,
};
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<TLane, TNumber> operator /(in Vector3<TLane, TNumber> left, in Vector3<TLane, TNumber> right)
{
return new Vector3<TLane, TNumber>
{
x = left.x / right.x,
y = left.y / right.y,
z = left.z / right.z,
};
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<TLane, TNumber> operator == (in Vector3<TLane, TNumber> left, in Vector3<TLane, TNumber> right)
{
return new Vector3<TLane, TNumber>
{
x = left.x == right.x,
y = left.y == right.y,
z = left.z == right.z,
};
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector3<TLane, TNumber> operator != (in Vector3<TLane, TNumber> left, in Vector3<TLane, TNumber> right)
{
return new Vector3<TLane, TNumber>
{
x = left.x != right.x,
y = left.y != right.y,
z = left.z != right.z,
};
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly bool Equals(Vector3<TLane, TNumber> 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<TLane, TNumber> other && Equals(other);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override int GetHashCode()
{
throw new NotImplementedException();
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -17,6 +17,9 @@ public class MathematicsBenchmark
private float4 _fa = new float4(1, 2, 1, 2); private float4 _fa = new float4(1, 2, 1, 2);
private float4 _fb = new float4(3, 4, 3, 4); private float4 _fb = new float4(3, 4, 3, 4);
private Vector64<float> _va64 = Vector64.Create(1f, 2f);
private Vector64<float> _vb64 = Vector64.Create(3f, 4f);
[Benchmark] [Benchmark]
public Vector4 VectorAdd() public Vector4 VectorAdd()
{ {
@@ -28,7 +31,7 @@ public class MathematicsBenchmark
return _va; return _va;
} }
[Benchmark] //[Benchmark]
public float4 floatAdd() public float4 floatAdd()
{ {
for (var i = 0; i < 10; i++) for (var i = 0; i < 10; i++)
@@ -38,6 +41,17 @@ public class MathematicsBenchmark
return _fa; return _fa;
} }
[Benchmark]
public Vector64<float> Vector64Add()
{
for (var i = 0; i < 10; i++)
{
_va64 += _vb64;
}
return _va64;
}
#endif #endif
#if FMA_BENCHMARK #if FMA_BENCHMARK

View File

@@ -1,4 +1,5 @@
using Misaki.HighPerformance.Jobs; using Misaki.HighPerformance.Jobs;
using Misaki.HighPerformance.LowLevel.Utilities;
using Misaki.HighPerformance.Mathematics; using Misaki.HighPerformance.Mathematics;
using Misaki.HighPerformance.Mathematics.SPMD; using Misaki.HighPerformance.Mathematics.SPMD;
using System.Numerics; using System.Numerics;
@@ -60,8 +61,7 @@ internal unsafe struct NoiseJobMath : IJobParallelFor
private static float2 GradientNoiseDirect(float2 uv) private static float2 GradientNoiseDirect(float2 uv)
{ {
uv.x %= 289; uv %= 289;
uv.y %= 289;
var x = (34 * uv.x + 1) * uv.x % 289 + uv.y; var x = (34 * uv.x + 1) * uv.x % 289 + uv.y;
x = (34 * x + 1) * x % 289; x = (34 * x + 1) * x % 289;
x = math.frac(x / 41) * 2 - 1; x = math.frac(x / 41) * 2 - 1;
@@ -261,7 +261,7 @@ internal unsafe struct NoiseJobMathSPMD : IJobSPMD<float>
var uvX = (indices % w) / w; var uvX = (indices % w) / w;
var uvY = TLane.Floor(indices / w) / h; var uvY = TLane.Floor(indices / w) / h;
var result = Noise(uvX, uvY); var result = Noise(uvX, uvY);
result.Store(buffers + baseIndex); result.Store(buffers + baseIndex);
} }

View File

@@ -1,2 +1 @@
BenchmarkDotNet.Running.BenchmarkRunner.Run<Misaki.HighPerformance.Test.Benchmark.SPMDBenchmark>(); BenchmarkDotNet.Running.BenchmarkRunner.Run<Misaki.HighPerformance.Test.Benchmark.MathematicsBenchmark>();
return;

View File

@@ -39,7 +39,7 @@ public static class CompressStoreTest
private unsafe static void TestPattern_Double(double[] input, bool[] keepPattern) private unsafe static void TestPattern_Double(double[] input, bool[] keepPattern)
{ {
// 1. Setup Input Vector // 1. Setup Input Vector
// Handle case where Vector<T> is smaller than 8 (e.g. 2 or 4) // Handle case where Vector<TLane> is smaller than 8 (e.g. 2 or 4)
var vecSize = Vector<double>.Count; var vecSize = Vector<double>.Count;
var safeInput = new double[vecSize]; var safeInput = new double[vecSize];
var safeMaskVal = new double[vecSize]; var safeMaskVal = new double[vecSize];

View File

@@ -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<float>
{
public float3* arrayA; // source array 1
public float3* arrayB; // source array 2
public float* results; // output array (dot products)
public readonly void Execute<TLane>(int baseIndex, int threadIndex)
where TLane : ISPMD<TLane, float>
{
var vecA = MathV.LoadVector3<TLane, float>((float*)(arrayA + baseIndex));
var vecB = MathV.LoadVector3<TLane, float>((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<DotProductJob, float>(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);
}
}

View File

@@ -1,15 +0,0 @@
using System.Runtime.InteropServices;
namespace Misaki.HighPerformance;
[StructLayout(LayoutKind.Explicit)]
public struct Union<T0, T1>
where T0 : unmanaged
where T1 : unmanaged
{
[FieldOffset(0)]
public T0 v0;
[FieldOffset(0)]
public T1 v1;
}