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
{{
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 void Run<T, TNumber>(this ref T job, int totalCount, int threadIndex)
@@ -67,6 +81,8 @@ public static class IJobParallelForSPMDExtensions
public static JobHandle ScheduleParallelSPDM<T, TNumber>(this JobScheduler jobScheduler, ref T job, int totalCount, int batchSize, int threadIndex, JobHandle dependency)
where T : unmanaged, IJobSPMD<TNumber>
where TNumber : unmanaged, INumber<TNumber>, IMinMaxValue<TNumber>, IBitwiseOperators<TNumber, TNumber, TNumber>
{
if (WideLane.IsSupported)
{
var warper = new SPMDJobWrapper<T, TNumber>
{
@@ -77,4 +93,15 @@ public static class IJobParallelForSPMDExtensions
var iterations = (totalCount + WideLane<TNumber>.LaneWidth - 1) / WideLane<TNumber>.LaneWidth;
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.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
/// <summary>
/// Represents a single-lane or multi-lane (vectorized) SPMD value and the operations supported on it.
/// </summary>
/// <typeparam name="TSelf">The concrete SPMD lane type implementing this interface.</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 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>
static abstract TSelf operator +(TSelf a, TSelf b);
/// <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.
/// </summary>
/// <param name="a">The first lane value.</param>
@@ -173,13 +172,6 @@ public interface ISPMD<TSelf, TNumber> : ISPMD
/// <returns>The lane-wise difference.</returns>
static abstract TSelf operator -(TSelf a, TSelf b);
/// <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.
/// </summary>
/// <param name="a">The first lane value.</param>
@@ -187,13 +179,6 @@ public interface ISPMD<TSelf, TNumber> : ISPMD
/// <returns>The lane-wise product.</returns>
static abstract TSelf operator *(TSelf a, TSelf b);
/// <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.
/// </summary>
/// <param name="a">The first lane value.</param>
@@ -201,26 +186,12 @@ public interface ISPMD<TSelf, TNumber> : ISPMD
/// <returns>The lane-wise quotient.</returns>
static abstract TSelf operator /(TSelf a, TSelf b);
/// <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.
/// </summary>
/// <param name="a">The first lane value.</param>
/// <param name="b">The second lane value.</param>
/// <returns>The lane-wise modulus.</returns>
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>
/// Negates the lane value element-wise.
@@ -237,13 +208,6 @@ public interface ISPMD<TSelf, TNumber> : ISPMD
/// <returns>The result of the bitwise AND.</returns>
static abstract TSelf operator &(TSelf a, TSelf b);
/// <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.
/// </summary>
/// <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>
static abstract TSelf operator |(TSelf a, TSelf b);
/// <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.
/// </summary>
/// <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>
static abstract TSelf operator ^(TSelf a, TSelf b);
/// <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.
/// </summary>
/// <param name="a">The lane value.</param>
/// <returns>The bitwise complement of the lane value.</returns>
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>
/// 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;
[StructLayout(LayoutKind.Sequential)]
public readonly unsafe struct ScalarLane<T> : ISPMD<ScalarLane<T>, T>
where T : unmanaged, INumber<T>, IMinMaxValue<T>, IBitwiseOperators<T, T, T>
public readonly unsafe struct ScalarLane<TNumber> : ISPMD<ScalarLane<TNumber>, TNumber>
where TNumber : unmanaged, INumber<TNumber>, IMinMaxValue<TNumber>, IBitwiseOperators<TNumber, TNumber, TNumber>
{
public readonly T value;
public readonly TNumber value;
public static int LaneWidth
{
@@ -16,77 +16,99 @@ public readonly unsafe struct ScalarLane<T> : ISPMD<ScalarLane<T>, T>
get => 1;
}
public static ScalarLane<T> Zero
public static ScalarLane<TNumber> Zero
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => new(T.Zero);
get => new(TNumber.Zero);
}
public static ScalarLane<T> One
public static ScalarLane<TNumber> One
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => new(T.One);
get => new(TNumber.One);
}
public static ScalarLane<T> MinValue
public static ScalarLane<TNumber> MinValue
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => new(T.MinValue);
get => new(TNumber.MinValue);
}
public static ScalarLane<T> MaxValue
public static ScalarLane<TNumber> 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<T> Create(T value) => new(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)
public static ScalarLane<TNumber> Create(TNumber value)
{
return CompressStore(mask, (T*)Unsafe.AsPointer(in destination));
return new(value);
}
[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;
return 1;
@@ -96,299 +118,358 @@ public readonly unsafe struct ScalarLane<T> : ISPMD<ScalarLane<T>, T>
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly Vector<T> AsVector() => Vector.Create(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, 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)
public readonly Vector<TNumber> AsVector()
{
// Note: INumber<T> 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<float>, 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<TNumber> operator +(ScalarLane<TNumber> a, ScalarLane<TNumber> b)
{
var f = Unsafe.As<ScalarLane<T>, float>(ref value);
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<TNumber>, float>(ref value);
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);
return Unsafe.As<double, ScalarLane<T>>(ref result);
return Unsafe.As<double, ScalarLane<TNumber>>(ref result);
}
return value;
}
[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)]
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);
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);
return Unsafe.As<double, ScalarLane<T>>(ref result);
return Unsafe.As<double, ScalarLane<TNumber>>(ref result);
}
return value;
}
[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);
[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)
public static ScalarLane<TNumber> Lerp(ScalarLane<TNumber> a, ScalarLane<TNumber> b, ScalarLane<TNumber> t)
{
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)
{
var f = Unsafe.As<ScalarLane<T>, float>(ref value);
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<TNumber>, float>(ref value);
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);
return Unsafe.As<double, ScalarLane<T>>(ref result);
return Unsafe.As<double, ScalarLane<TNumber>>(ref result);
}
return value;
}
[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);
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);
return Unsafe.As<double, ScalarLane<T>>(ref result);
return Unsafe.As<double, ScalarLane<TNumber>>(ref result);
}
return value;
}
[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)]
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);
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);
return Unsafe.As<double, ScalarLane<T>>(ref result);
return Unsafe.As<double, ScalarLane<TNumber>>(ref result);
}
return value;
}
[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);
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);
return Unsafe.As<double, ScalarLane<T>>(ref result);
return Unsafe.As<double, ScalarLane<TNumber>>(ref result);
}
return value;
}
[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);
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);
return Unsafe.As<double, ScalarLane<T>>(ref result);
return Unsafe.As<double, ScalarLane<TNumber>>(ref result);
}
return value;
}
[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);
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);
return Unsafe.As<double, ScalarLane<T>>(ref result);
return Unsafe.As<double, ScalarLane<TNumber>>(ref result);
}
return value;
}
[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 fx = Unsafe.As<ScalarLane<T>, float>(ref x);
var fy = Unsafe.As<ScalarLane<TNumber>, float>(ref y);
var fx = Unsafe.As<ScalarLane<TNumber>, float>(ref x);
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 dx = Unsafe.As<ScalarLane<T>, double>(ref x);
var dy = Unsafe.As<ScalarLane<TNumber>, double>(ref y);
var dx = Unsafe.As<ScalarLane<TNumber>, double>(ref x);
var result = Math.Atan2(dy, dx);
return Unsafe.As<double, ScalarLane<T>>(ref result);
return Unsafe.As<double, ScalarLane<TNumber>>(ref result);
}
return y;
}
[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 fy = Unsafe.As<ScalarLane<T>, float>(ref y);
var fx = Unsafe.As<ScalarLane<TNumber>, float>(ref x);
var fy = Unsafe.As<ScalarLane<TNumber>, float>(ref y);
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 dy = Unsafe.As<ScalarLane<T>, double>(ref y);
var dx = Unsafe.As<ScalarLane<TNumber>, double>(ref x);
var dy = Unsafe.As<ScalarLane<TNumber>, double>(ref y);
var result = Math.Pow(dx, dy);
return Unsafe.As<double, ScalarLane<T>>(ref result);
return Unsafe.As<double, ScalarLane<TNumber>>(ref result);
}
return x;
}
[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);
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);
return Unsafe.As<double, ScalarLane<T>>(ref result);
return Unsafe.As<double, ScalarLane<TNumber>>(ref result);
}
return value;
@@ -396,167 +477,222 @@ public readonly unsafe struct ScalarLane<T> : ISPMD<ScalarLane<T>, T>
[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)]
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);
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);
return Unsafe.As<double, ScalarLane<T>>(ref result);
return Unsafe.As<double, ScalarLane<TNumber>>(ref result);
}
return value;
}
[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);
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);
return Unsafe.As<double, ScalarLane<T>>(ref result);
return Unsafe.As<double, ScalarLane<TNumber>>(ref result);
}
return value;
}
[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);
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);
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);
return Unsafe.As<decimal, ScalarLane<T>>(ref result);
return Unsafe.As<decimal, ScalarLane<TNumber>>(ref result);
}
return value;
}
[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);
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);
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);
return Unsafe.As<decimal, ScalarLane<T>>(ref result);
return Unsafe.As<decimal, ScalarLane<TNumber>>(ref result);
}
return value;
}
[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);
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);
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);
return Unsafe.As<decimal, ScalarLane<T>>(ref result);
return Unsafe.As<decimal, ScalarLane<TNumber>>(ref result);
}
return value;
}
[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)]
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)]
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)]
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)]
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)]
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)]
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)]
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()
{
return value.ToString() ?? string.Empty;

View File

@@ -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()
{

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 _fb = new float4(3, 4, 3, 4);
private Vector64<float> _va64 = Vector64.Create(1f, 2f);
private Vector64<float> _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<float> Vector64Add()
{
for (var i = 0; i < 10; i++)
{
_va64 += _vb64;
}
return _va64;
}
#endif
#if FMA_BENCHMARK

View File

@@ -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;

View File

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

View File

@@ -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<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 safeInput = 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;
}