rewrite of f32 fma, and other optmization of simd kernel ops

This commit is contained in:
Youmu Liu
2025-11-10 20:40:19 +08:00
parent 1dee467220
commit 678a846e54
18 changed files with 302 additions and 156 deletions
@@ -91,7 +91,7 @@ namespace StgSharp.Collections
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ReadOnlySpan<T> PopRange(int count)
public T[] PopRange(int count)
{
int i = int.Min(count, _index) - 1;
return _values[0..i];
@@ -34,12 +34,65 @@ using System.Threading.Tasks;
namespace StgSharp.HighPerformance
{
public interface IRegisterType
/// <summary>
/// Abstraction for a wide SIMD register or equivalent packed numeric storage. Provides raw
/// reference access, broadcast initialization and element level access. Typical
/// implementations map to 128/256/512-bit hardware registers or software emulated blocks.
/// </summary>
public interface IRegisterPresentation
{
/// <summary>
/// Gets a reference to the first element of the register reinterpreted as type <typeparamref
/// name="T" />.
/// </summary>
/// <typeparam name="T">
/// Unmanaged numeric element type implementing <see cref="INumber{T}" />.
/// </typeparam>
/// <returns>
/// Writable reference to element 0 (lowest lane / lowest bits).
/// </returns>
/// <remarks>
/// Intended for treating the register as a contiguous block of elements. The returned reference
/// must remain valid for the lifetime of the register and be identical to <see
/// cref="Member{T}(int)" /> with index 0. Caller must choose a type layout compatible with the
/// actual register representation.
/// </remarks>
ref T AsRef<T>() where T: unmanaged, INumber<T>;
/// <summary>
/// Fills all elements (lanes) of the register with the provided scalar <paramref name="value" />.
/// </summary>
/// <typeparam name="T">
/// Unmanaged numeric element type implementing <see cref="INumber{T}" />.
/// </typeparam>
/// <param name="value">
/// Scalar value to replicate to every element.
/// </param>
/// <remarks>
/// Should map to a single broadcast/shuffle instruction where possible. After completion all
/// lanes read back the same value.
/// </remarks>
void BroadCastFrom<T>(T value) where T: unmanaged, INumber<T>;
/// <summary>
/// Gets a writable reference to the element at the specified <paramref name="index" />.
/// </summary>
/// <typeparam name="T">
/// Unmanaged numeric element type implementing <see cref="INumber{T}" />.
/// </typeparam>
/// <param name="index">
/// Zero-based lane index (range defined by implementation).
/// </param>
/// <returns>
/// Writable reference to the lane.
/// </returns>
/// <remarks>
/// Enables scalar lane edits without unpacking the entire register. Implementations may omit
/// bounds checks for performance. Caller is responsible for passing a valid index.
/// </remarks>
ref T Member<T>(int index) where T: unmanaged, INumber<T>;
}
}
}
+5
View File
@@ -68,6 +68,11 @@ namespace StgSharp.HighPerformance
return Vec.AsVector4();
}
public void BroadCastFrom<T>(T value) where T: unmanaged, INumber<T>
{
Vec = Vector128.CreateScalarUnsafe(value).As<T, float>();
}
public override readonly bool Equals([NotNullWhen(true)] object obj)
{
return obj is M128 other && this == other;
+7
View File
@@ -31,6 +31,7 @@ using System.Linq;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
using System.Text;
using System.Threading.Tasks;
@@ -41,6 +42,7 @@ namespace StgSharp.HighPerformance
{
[FieldOffset(0)] internal fixed byte Buffer[32];
[FieldOffset(0)] internal Vector256<float> Vec;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref T AsRef<T>() where T: unmanaged, INumber<T>
@@ -48,6 +50,11 @@ namespace StgSharp.HighPerformance
return ref Unsafe.As<byte, T>(ref Buffer[0]);
}
public void BroadCastFrom<T>(T value) where T: unmanaged, INumber<T>
{
Vec = Vector256.CreateScalarUnsafe(value).As<T, float>();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref T Member<T>(int index) where T: unmanaged, INumber<T>
{
+7
View File
@@ -31,6 +31,7 @@ using System.Linq;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
using System.Text;
using System.Threading.Tasks;
@@ -41,12 +42,18 @@ namespace StgSharp.HighPerformance
{
[FieldOffset(0)]private fixed byte _buffer[64];
[FieldOffset(0)]private Vector512<float> Vec;
public ref T AsRef<T>() where T: unmanaged, INumber<T>
{
return ref Unsafe.As<byte, T>(ref _buffer[0]);
}
public void BroadCastFrom<T>(T value) where T: unmanaged, INumber<T>
{
Vec = Vector512.CreateScalarUnsafe(value).As<T, float>();
}
public ref T Member<T>(int index) where T: unmanaged, INumber<T>
{
return ref Unsafe.As<byte, T>(ref _buffer[0]);
@@ -1,6 +1,6 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="IRegisterPresentation"
// file="ScalarPacket"
// Project: StgSharp
// AuthorGroup: Nitload
// Copyright (c) Nitload. All rights reserved.
@@ -29,17 +29,36 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
namespace StgSharp.HighPerformance
{
public interface IRegisterPresentation
public unsafe struct ScalarPacket
{
public ref T AsRef<T>() where T: unmanaged, INumber<T>;
private fixed ulong data[8];
public ref T Member<T>(int index) where T: unmanaged, INumber<T>;
public void BroadCast<TScalar, TRegister>(int slotIndex, TScalar value)
where TScalar: unmanaged, INumber<TScalar>
where TRegister: unmanaged, IRegisterPresentation
{
if (slotIndex is < 0 or >= 8) {
throw new IndexOutOfRangeException();
}
ref TRegister register = ref Unsafe.As<ulong, TRegister>(ref data[slotIndex]);
register.BroadCastFrom(value);
}
public ref T Data<T>(int index) where T: unmanaged, INumber<T>
{
if (index is < 0 or >= 8) {
throw new IndexOutOfRangeException();
}
return ref Unsafe.As<ulong, T>(ref data[index]);
}
}
}
@@ -26,6 +26,7 @@
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using StgSharp.HighPerformance;
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@@ -38,8 +39,9 @@ namespace StgSharp.Internal.Intrinsic
public delegate* unmanaged[Cdecl]<char*, int, int> city_hash_simplify;
public delegate* unmanaged[Cdecl]<void*, void*, void*, void> f32_add;
public delegate* unmanaged[Cdecl]<void*, ScalarPacket*, void> f32_fill;
public delegate* unmanaged[Cdecl]<void*, void*, void*, void> f32_fma;
public delegate* unmanaged[Cdecl]<void*, float, void*, void> f32_scalar_mul;
public delegate* unmanaged[Cdecl]<void*, void*, ScalarPacket*, void> f32_scalar_mul;
public delegate* unmanaged[Cdecl]<void*, void*, void*, void> f32_sub;
public delegate* unmanaged[Cdecl]<void*, void*, void> f32_transpose;
public delegate* unmanaged[Cdecl]<int, ulong> factorial_simd;
@@ -52,4 +54,4 @@ namespace StgSharp.Internal.Intrinsic
}
}
}
}