dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
15.36k stars 4.74k forks source link

[API Proposal]: VectorXXX<Half> support, and related various Hardware Intrinsics(`F16C`, `AVX-512 FP16`, and `AdvSimd.Arm64`) #62416

Open MineCake147E opened 2 years ago

MineCake147E commented 2 years ago

Background and motivation

It's been a long time .NET first added Half type.
But there's no support for hardware acceleration of conversion, that might be a common use case.
In Ivy Bridge and newer x86 processors, F16C is provided as a way to convert between float and Half.
ARMv8-A also has a way to convert between them like F16C.
In Sapphire Rapids and newer x86 processors, AVX-512 FP16 is provided as a way to perform arithmetic operations of Half values.
So I think it's great if .NET had support for hardware acceleration of conversion between them.

API Proposal

EDIT: Vector64<Half> design is removed as it's avoided in x86.
EDIT: Added some arithmetic APIs including MultiplyAddEstimate discussed in #98053.

Addition to Vector*

namespace System.Numerics
{
    public static class Vector
    {
        public static Vector<Half> AsVectorHalf<T>(Vector<T> value);
        public static Vector<float> Ceiling (Vector<float> value);
        public static Vector<Half> Floor(Vector<Half> vector);
        public static Vector<short> ConvertToInt16(Vector<Half> value);
        public static Vector<Half> ConvertToHalf(Vector<short> value);
        public static Vector<Half> MultiplyAddEstimate(Vector<Half> x, Vector<Half> y, Vector<Half> addend);
        public static Vector<Half> Narrow(Vector<float> low, Vector<float> high);
        public static void Widen(Vector<Half> source, out Vector<float> low, out Vector<float> high);
    }
}
namespace System.Runtime.Intrinsics
{
    public static class Vector64
    {
        public static Vector64<Half> AsHalf<T>(this Vector64<T> vector) where T : struct;
        public static Vector64<Half> Create(Half e0, Half e1, Half e2, Half e3);
        public static Vector64<Half> Create(Half value);
        public static Vector64<Half> Ceiling(Vector64<Half> vector);
        public static Vector64<Half> Floor(Vector64<Half> vector);
        public static Vector64<Half> CreateScalar(Half value);
        public static Vector64<Half> CreateScalarUnsafe(Half value);
        public static Vector64<short> ConvertToInt16(Vector64<Half> vector);
        public static Vector64<ushort> ConvertToUInt16(Vector64<Half> vector);
        public static Vector64<Half> ConvertToHalf(Vector64<short> vector);
        public static Vector64<Half> ConvertToHalf(Vector64<ushort> vector);
        public static Vector64<Half> MultiplyAddEstimate(Vector64<Half> x, Vector64<Half> y, Vector64<Half> addend);
    }
    public static class Vector128
    {
        public static Vector128<Half> AsHalf<T>(this Vector128<T> vector) where T : struct;
        public static Vector128<Half> Create(Half e0, Half e1, Half e2, Half e3, Half e4, Half e5, Half e6, Half e7);
        public static Vector128<Half> Create(Half value);
        public static Vector128<Half> Create(Vector64<Half> lower, Vector64<Half> upper);
        public static Vector128<Half> Ceiling(Vector128<Half> vector);
        public static Vector128<Half> Floor(Vector128<Half> vector);
        public static Vector128<Half> CreateScalar(Half value);
        public static Vector128<Half> CreateScalarUnsafe(Half value);
        public static Vector128<short> ConvertToInt16(Vector128<Half> vector);
        public static Vector128<ushort> ConvertToUInt16(Vector128<Half> vector);
        public static Vector128<Half> ConvertToHalf(Vector128<short> vector);
        public static Vector128<Half> ConvertToHalf(Vector128<ushort> vector);
        public static Vector128<Half> MultiplyAddEstimate(Vector128<Half> x, Vector128<Half> y, Vector128<Half> addend);
    }
    public static class Vector256
    {
        public static Vector256<Half> AsHalf<T>(this Vector256<T> vector) where T : struct;
        public static Vector256<Half> Create(Half e0, Half e1, Half e2, Half e3, Half e4, Half e5, Half e6, Half e7, Half e8, Half e9, Half e10, Half e11, Half e12, Half e13, Half e14, Half e15);
        public static Vector256<Half> Create(Half value);
        public static Vector256<Half> Create(Vector128<Half> lower, Vector128<Half> upper);
        public static Vector256<Half> Ceiling(Vector256<Half> vector);
        public static Vector256<Half> Floor(Vector256<Half> vector);
        public static Vector256<Half> CreateScalar(Half value);
        public static Vector256<Half> CreateScalarUnsafe(Half value);
        public static Vector256<short> ConvertToInt16(Vector256<Half> vector);
        public static Vector256<ushort> ConvertToUInt16(Vector256<Half> vector);
        public static Vector256<Half> ConvertToHalf(Vector256<short> vector);
        public static Vector256<Half> ConvertToHalf(Vector256<ushort> vector);
        public static Vector256<Half> MultiplyAddEstimate(Vector256<Half> x, Vector256<Half> y, Vector256<Half> addend);
    }
    public static class Vector512
    {
        public static Vector512<Half> AsHalf<T>(this Vector512<T> vector) where T : struct;
        public static Vector512<Half> Create(Half e0, Half e1, Half e2, Half e3, Half e4, Half e5, Half e6, Half e7, Half e8, Half e9, Half e10, Half e11, Half e12, Half e13, Half e14, Half e15, Half e16, Half e17, Half e18, Half e19, Half e20, Half e21, Half e22, Half e23, Half e24, Half e25, Half e26, Half e27, Half e28, Half e29, Half e30, Half e31);
        public static Vector512<Half> Create(Half value);
        public static Vector512<Half> Create(Vector256<Half> lower, Vector256<Half> upper);
        public static Vector512<Half> Ceiling(Vector512<Half> vector);
        public static Vector512<Half> Floor(Vector512<Half> vector);
        public static Vector512<Half> CreateScalar(Half value);
        public static Vector512<Half> CreateScalarUnsafe(Half value);
        public static Vector512<short> ConvertToInt16(Vector512<Half> vector);
        public static Vector512<ushort> ConvertToUInt16(Vector512<Half> vector);
        public static Vector512<Half> ConvertToHalf(Vector512<short> vector);
        public static Vector512<Half> ConvertToHalf(Vector512<ushort> vector);
        public static Vector512<Half> MultiplyAddEstimate(Vector512<Half> x, Vector512<Half> y, Vector512<Half> addend);
    }
}

Vector(64|128|256|512)?<T> shouldn't throw any exceptions if T was Half.

F16C

namespace System.Runtime.Intrinsics.X86
{
    public abstract class F16C : Avx
    {
        public static Vector128<float> ConvertToVector128Single(Vector128<Half> value);
        public static Vector256<float> ConvertToVector256Single(Vector128<Half> value);
        public static Vector128<Half> ConvertToVector128Half(Vector128<float> value);
        public static Vector128<Half> ConvertToVector128Half(Vector256<float> value);
        public static Vector128<Half> ConvertToVector128HalfRoundToEven(Vector128<float> value);
        public static Vector128<Half> ConvertToVector128HalfRoundToEven(Vector256<float> value);
        public static Vector128<Half> ConvertToVector128HalfRoundToZero(Vector128<float> value);
        public static Vector128<Half> ConvertToVector128HalfRoundToZero(Vector256<float> value);
        public static Vector128<Half> ConvertToVector128HalfRoundToNegativeInfinity(Vector128<float> value);
        public static Vector128<Half> ConvertToVector128HalfRoundToNegativeInfinity(Vector256<float> value);
        public static Vector128<Half> ConvertToVector128HalfRoundToPositiveInfinity(Vector128<float> value);
        public static Vector128<Half> ConvertToVector128HalfRoundToPositiveInfinity(Vector256<float> value);
    }
}

AVX-512 FP16

namespace System.Runtime.Intrinsics.X86;

public abstract class Avx512Fp16 : F16C
{
    public static Vector512<Half> Add(Vector512<Half> left, Vector512<Half> right);
    public static Vector512<Half> BlendVariable(Vector512<Half> left, Vector512<Half> right, Vector512<Half> mask);
    public static Vector512<Half> BroadcastScalarToVector512(Vector128<Half> value);
    public static Vector512<Half> Classify(Vector512<Half> value, [ConstantExpected] byte control);
    public static Vector128<Half> ClassifyScalar(Vector128<Half> value, [ConstantExpected] byte control);
    public static Vector512<Half> Compare(Vector512<Half> left, Vector512<Half> right, [ConstantExpected(Max = FloatComparisonMode.UnorderedTrueSignaling)] FloatComparisonMode mode);
    public static Vector512<Half> CompareEqual(Vector512<Half> left, Vector512<Half> right);
    public static Vector512<Half> CompareGreaterThan(Vector512<Half> left, Vector512<Half> right);
    public static Vector512<Half> CompareGreaterThanOrEqual(Vector512<Half> left, Vector512<Half> right);
    public static Vector512<Half> CompareLessThan(Vector512<Half> left, Vector512<Half> right);
    public static Vector512<Half> CompareLessThanOrEqual(Vector512<Half> left, Vector512<Half> right);
    public static Vector512<Half> CompareNotEqual(Vector512<Half> left, Vector512<Half> right);
    public static Vector512<Half> CompareNotGreaterThan(Vector512<Half> left, Vector512<Half> right);
    public static Vector512<Half> CompareNotGreaterThanOrEqual(Vector512<Half> left, Vector512<Half> right);
    public static Vector512<Half> CompareNotLessThan(Vector512<Half> left, Vector512<Half> right);
    public static Vector512<Half> CompareNotLessThanOrEqual(Vector512<Half> left, Vector512<Half> right);
    public static Vector512<Half> CompareOrdered(Vector512<Half> left, Vector512<Half> right);
    public static Vector512<Half> CompareUnordered(Vector512<Half> left, Vector512<Half> right);
    public static Vector512<Half> ComplexMultiply(Vector512<Half> left, Vector512<Half> right);
    public static Vector512<Half> ComplexMultiplyConjugate(Vector512<Half> left, Vector512<Half> right);
    public static Vector512<Half> ComplexMultiplyAccumulate(Vector512<Half> a, Vector512<Half> b, Vector512<Half> c);
    public static Vector512<Half> ComplexMultiplyConjugateAccumulate(Vector512<Half> a, Vector512<Half> b, Vector512<Half> c);
    public static Vector512<Half> ConvertToVector512Half(Vector512<short> value);
    public static Vector512<Half> ConvertToVector512Half(Vector512<ushort> value);
    public static Vector256<Half> ConvertToVector256Half(Vector512<int> value);
    public static Vector256<Half> ConvertToVector256Half(Vector512<uint> value);
    public static Vector128<Half> ConvertToVector128Half(Vector512<long> value);
    public static Vector128<Half> ConvertToVector128Half(Vector512<ulong> value);
    public static Vector256<Half> ConvertToVector256Half(Vector512<float> value);
    public static Vector128<Half> ConvertToVector128Half(Vector512<double> value);
    public static Vector512<short> ConvertToVector512Int16(Vector512<Half> value);
    public static Vector512<short> ConvertToVector512Int16WithTruncation(Vector512<Half> value);
    public static Vector512<ushort> ConvertToVector512UInt16(Vector512<Half> value);
    public static Vector512<ushort> ConvertToVector512UInt16WithTruncation(Vector512<Half> value);
    public static Vector512<int> ConvertToVector512Int32(Vector256<Half> value);
    public static Vector512<int> ConvertToVector512Int32WithTruncation(Vector256<Half> value);
    public static Vector512<uint> ConvertToVector512UInt32(Vector256<Half> value);
    public static Vector512<uint> ConvertToVector512UInt32WithTruncation(Vector256<Half> value);
    public static Vector512<long> ConvertToVector512Int64(Vector128<Half> value);
    public static Vector512<long> ConvertToVector512Int64WithTruncation(Vector128<Half> value);
    public static Vector512<ulong> ConvertToVector512UInt64(Vector128<Half> value);
    public static Vector512<ulong> ConvertToVector512UInt64WithTruncation(Vector128<Half> value);
    public static Vector512<float> ConvertToVector512Single(Vector256<Half> value);
    public static Vector512<double> ConvertToVector512Double(Vector128<Half> value);
    public static Vector512<Half> Divide(Vector512<Half> left, Vector512<Half> right);
    public static Vector512<Half> FusedMultiplyAdd(Vector512<Half> a, Vector512<Half> b, Vector512<Half> c);
    public static Vector512<Half> FusedMultiplyAddSubtract(Vector512<Half> a, Vector512<Half> b, Vector512<Half> c);
    public static Vector512<Half> FusedMultiplySubtract(Vector512<Half> a, Vector512<Half> b, Vector512<Half> c);
    public static Vector512<Half> FusedMultiplySubtractAdd(Vector512<Half> a, Vector512<Half> b, Vector512<Half> c);
    public static Vector512<Half> FusedMultiplyAddNegated(Vector512<Half> a, Vector512<Half> b, Vector512<Half> c);
    public static Vector512<Half> FusedMultiplySubtractNegated(Vector512<Half> a, Vector512<Half> b, Vector512<Half> c);
    public static Vector512<Half> GetExponent(Vector512<Half> value);
    public static Vector512<Half> GetMantissa(Vector512<Half> value, [ConstantExpected(Max = (byte)(0x0F))] byte control);
    public static Vector512<Half> InsertVector128(Vector512<Half> value, Vector128<Half> data, [ConstantExpected] byte index);
    public static Vector512<Half> InsertVector256(Vector512<Half> value, Vector256<Half> data, [ConstantExpected] byte index);
    public static Vector512<Half> Max(Vector512<Half> left, Vector512<Half> right);
    public static Vector512<Half> Min(Vector512<Half> left, Vector512<Half> right);
    public static Vector512<Half> Multiply(Vector512<Half> left, Vector512<Half> right);
    public static Vector512<Half> Reciprocal(Vector512<Half> value);
    public static Vector512<Half> ReciprocalSqrt(Vector512<Half> value);
    public static Vector512<Half> RoundScale(Vector512<Half> value, [ConstantExpected] byte control);
    public static Vector512<Half> Scale(Vector512<Half> left, Vector512<Half> right);
    public static Vector512<Half> Sqrt(Vector512<Half> value);
    public static Vector512<Half> Subtract(Vector512<Half> left, Vector512<Half> right);
    public static Vector512<Half> TernaryLogic(Vector512<Half> a, Vector512<Half> b, Vector512<Half> c, [ConstantExpected] byte control);
    public static Vector512<Half> And(Vector512<Half> left, Vector512<Half> right);
    public static Vector512<Half> AndNot(Vector512<Half> left, Vector512<Half> right);
    public static Vector512<Half> Or(Vector512<Half> left, Vector512<Half> right);
    public static Vector512<Half> Reduce(Vector512<Half> value, [ConstantExpected] byte control);
    public static Vector512<Half> Xor(Vector512<Half> left, Vector512<Half> right);
    public static Vector128<Half> AddScalar(Vector128<Half> left, Vector128<Half> right);
    public static Vector128<Half> CompareScalar(Vector128<Half> left, Vector128<Half> right, [ConstantExpected(Max = FloatComparisonMode.UnorderedTrueSignaling)] FloatComparisonMode mode);
    public static Vector128<Half> CompareEqualScalar(Vector128<Half> left, Vector128<Half> right);
    public static Vector128<Half> CompareGreaterThanScalar(Vector128<Half> left, Vector128<Half> right);
    public static Vector128<Half> CompareGreaterThanOrEqualScalar(Vector128<Half> left, Vector128<Half> right);
    public static Vector128<Half> CompareLessThanScalar(Vector128<Half> left, Vector128<Half> right);
    public static Vector128<Half> CompareLessThanOrEqualScalar(Vector128<Half> left, Vector128<Half> right);
    public static Vector128<Half> CompareNotEqualScalar(Vector128<Half> left, Vector128<Half> right);
    public static Vector128<Half> CompareNotGreaterThanScalar(Vector128<Half> left, Vector128<Half> right);
    public static Vector128<Half> CompareNotGreaterThanOrEqualScalar(Vector128<Half> left, Vector128<Half> right);
    public static Vector128<Half> CompareNotLessThanScalar(Vector128<Half> left, Vector128<Half> right);
    public static Vector128<Half> CompareNotLessThanOrEqualScalar(Vector128<Half> left, Vector128<Half> right);
    public static Vector128<Half> CompareOrderedScalar(Vector128<Half> left, Vector128<Half> right);
    public static Vector128<Half> CompareUnorderedScalar(Vector128<Half> left, Vector128<Half> right);
    public static Vector128<Half> DivideScalar(Vector128<Half> left, Vector128<Half> right);
    public static Vector128<Half> FusedMultiplyAddScalar(Vector128<Half> a, Vector128<Half> b, Vector128<Half> c);
    public static Vector128<Half> FusedMultiplySubtractScalar(Vector128<Half> a, Vector128<Half> b, Vector128<Half> c);
    public static Vector128<Half> FusedMultiplyAddNegatedScalar(Vector128<Half> a, Vector128<Half> b, Vector128<Half> c);
    public static Vector128<Half> FusedMultiplySubtractNegatedScalar(Vector128<Half> a, Vector128<Half> b, Vector128<Half> c);
    public static Vector128<Half> GetExponentScalar(Vector128<Half> value);
    public static Vector128<Half> GetMantissaScalar(Vector128<Half> value, [ConstantExpected(Max = (byte)(0x0F))] byte control);
    public static Vector128<Half> MaxScalar(Vector128<Half> left, Vector128<Half> right);
    public static Vector128<Half> MinScalar(Vector128<Half> left, Vector128<Half> right);
    public static Vector128<Half> MultiplyScalar(Vector128<Half> left, Vector128<Half> right);
    public static Vector128<Half> ReciprocalScalar(Vector128<Half> value);
    public static Vector128<Half> ReciprocalSqrtScalar(Vector128<Half> value);
    public static Vector128<Half> RoundScaleScalar(Vector128<Half> value, [ConstantExpected] byte control);
    public static Vector128<Half> ScaleScalar(Vector128<Half> left, Vector128<Half> right);
    public static Vector128<Half> SqrtScalar(Vector128<Half> value);
    public static Vector128<Half> SubtractScalar(Vector128<Half> left, Vector128<Half> right);

    public abstract class VL : Avx512DQ.VL
    {
        public static Vector256<Half> Add(Vector256<Half> left, Vector256<Half> right);
        public static Vector128<Half> Add(Vector128<Half> left, Vector128<Half> right);
        public static Vector256<Half> BlendVariable(Vector256<Half> left, Vector256<Half> right, Vector256<Half> mask);
        public static Vector128<Half> BlendVariable(Vector128<Half> left, Vector128<Half> right, Vector128<Half> mask);
        public static Vector256<Half> BroadcastScalarToVector256(Vector128<Half> value);
        public static Vector128<Half> BroadcastScalarToVector128(Vector128<Half> value);
        public static Vector256<Half> Classify(Vector256<Half> value, [ConstantExpected] byte control);
        public static Vector128<Half> Classify(Vector128<Half> value, [ConstantExpected] byte control);
        public static Vector256<Half> Compare(Vector256<Half> left, Vector256<Half> right, [ConstantExpected(Max = FloatComparisonMode.UnorderedTrueSignaling)] FloatComparisonMode mode);
        public static Vector128<Half> Compare(Vector128<Half> left, Vector128<Half> right, [ConstantExpected(Max = FloatComparisonMode.UnorderedTrueSignaling)] FloatComparisonMode mode);
        public static Vector256<Half> CompareEqual(Vector256<Half> left, Vector256<Half> right);
        public static Vector128<Half> CompareEqual(Vector128<Half> left, Vector128<Half> right);
        public static Vector256<Half> CompareGreaterThan(Vector256<Half> left, Vector256<Half> right);
        public static Vector128<Half> CompareGreaterThan(Vector128<Half> left, Vector128<Half> right);
        public static Vector256<Half> CompareGreaterThanOrEqual(Vector256<Half> left, Vector256<Half> right);
        public static Vector128<Half> CompareGreaterThanOrEqual(Vector128<Half> left, Vector128<Half> right);
        public static Vector256<Half> CompareLessThan(Vector256<Half> left, Vector256<Half> right);
        public static Vector128<Half> CompareLessThan(Vector128<Half> left, Vector128<Half> right);
        public static Vector256<Half> CompareLessThanOrEqual(Vector256<Half> left, Vector256<Half> right);
        public static Vector128<Half> CompareLessThanOrEqual(Vector128<Half> left, Vector128<Half> right);
        public static Vector256<Half> CompareNotEqual(Vector256<Half> left, Vector256<Half> right);
        public static Vector128<Half> CompareNotEqual(Vector128<Half> left, Vector128<Half> right);
        public static Vector256<Half> CompareNotGreaterThan(Vector256<Half> left, Vector256<Half> right);
        public static Vector128<Half> CompareNotGreaterThan(Vector128<Half> left, Vector128<Half> right);
        public static Vector256<Half> CompareNotGreaterThanOrEqual(Vector256<Half> left, Vector256<Half> right);
        public static Vector128<Half> CompareNotGreaterThanOrEqual(Vector128<Half> left, Vector128<Half> right);
        public static Vector256<Half> CompareNotLessThan(Vector256<Half> left, Vector256<Half> right);
        public static Vector128<Half> CompareNotLessThan(Vector128<Half> left, Vector128<Half> right);
        public static Vector256<Half> CompareNotLessThanOrEqual(Vector256<Half> left, Vector256<Half> right);
        public static Vector128<Half> CompareNotLessThanOrEqual(Vector128<Half> left, Vector128<Half> right);
        public static Vector256<Half> CompareOrdered(Vector256<Half> left, Vector256<Half> right);
        public static Vector128<Half> CompareOrdered(Vector128<Half> left, Vector128<Half> right);
        public static Vector256<Half> CompareUnordered(Vector256<Half> left, Vector256<Half> right);
        public static Vector128<Half> CompareUnordered(Vector128<Half> left, Vector128<Half> right);
        public static Vector256<Half> ComplexMultiply(Vector256<Half> left, Vector256<Half> right);
        public static Vector128<Half> ComplexMultiply(Vector128<Half> left, Vector128<Half> right);
        public static Vector256<Half> ComplexMultiplyConjugate(Vector256<Half> left, Vector256<Half> right);
        public static Vector128<Half> ComplexMultiplyConjugate(Vector128<Half> left, Vector128<Half> right);
        public static Vector256<Half> ComplexMultiplyAccumulate(Vector256<Half> a, Vector256<Half> b, Vector256<Half> c);
        public static Vector128<Half> ComplexMultiplyAccumulate(Vector128<Half> a, Vector128<Half> b, Vector128<Half> c);
        public static Vector256<Half> ComplexMultiplyConjugateAccumulate(Vector256<Half> a, Vector256<Half> b, Vector256<Half> c);
        public static Vector128<Half> ComplexMultiplyConjugateAccumulate(Vector128<Half> a, Vector128<Half> b, Vector128<Half> c);
        public static Vector256<Half> ConvertToVector256Half(Vector256<short> value);
        public static Vector128<Half> ConvertToVector128Half(Vector128<short> value);
        public static Vector256<Half> ConvertToVector256Half(Vector256<ushort> value);
        public static Vector128<Half> ConvertToVector128Half(Vector128<ushort> value);
        public static Vector128<Half> ConvertToVector128Half(Vector256<int> value);
        public static Vector128<Half> ConvertToVector128Half(Vector128<int> value);
        public static Vector128<Half> ConvertToVector128Half(Vector256<uint> value);
        public static Vector128<Half> ConvertToVector128Half(Vector128<uint> value);
        public static Vector128<Half> ConvertToVector128Half(Vector256<long> value);
        public static Vector128<Half> ConvertToVector128Half(Vector128<long> value);
        public static Vector128<Half> ConvertToVector128Half(Vector256<ulong> value);
        public static Vector128<Half> ConvertToVector128Half(Vector128<ulong> value);
        public static Vector128<Half> ConvertToVector128Half(Vector256<float> value);
        public static Vector128<Half> ConvertToVector128Half(Vector128<float> value);
        public static Vector128<Half> ConvertToVector128Half(Vector256<double> value);
        public static Vector128<Half> ConvertToVector128Half(Vector128<double> value);
        public static Vector256<short> ConvertToVector256Int16(Vector256<Half> value);
        public static Vector128<short> ConvertToVector128Int16(Vector128<Half> value);
        public static Vector256<short> ConvertToVector256Int16WithTruncation(Vector256<Half> value);
        public static Vector128<short> ConvertToVector128Int16WithTruncation(Vector128<Half> value);
        public static Vector256<short> ConvertToVector256UInt16(Vector256<Half> value);
        public static Vector128<short> ConvertToVector128UInt16(Vector128<Half> value);
        public static Vector256<short> ConvertToVector256UInt16WithTruncation(Vector256<Half> value);
        public static Vector128<short> ConvertToVector128UInt16WithTruncation(Vector128<Half> value);
        public static Vector256<int> ConvertToVector256Int32(Vector128<Half> value);
        public static Vector128<int> ConvertToVector128Int32(Vector128<Half> value);
        public static Vector256<int> ConvertToVector256Int32WithTruncation(Vector128<Half> value);
        public static Vector128<int> ConvertToVector128Int32WithTruncation(Vector128<Half> value);
        public static Vector256<uint> ConvertToVector256UInt32(Vector128<Half> value);
        public static Vector128<uint> ConvertToVector128UInt32(Vector128<Half> value);
        public static Vector256<uint> ConvertToVector256UInt32WithTruncation(Vector128<Half> value);
        public static Vector128<uint> ConvertToVector128UInt32WithTruncation(Vector128<Half> value);
        public static Vector256<long> ConvertToVector256Int64(Vector128<Half> value);
        public static Vector128<long> ConvertToVector128Int64(Vector128<Half> value);
        public static Vector256<long> ConvertToVector256Int64WithTruncation(Vector128<Half> value);
        public static Vector128<long> ConvertToVector128Int64WithTruncation(Vector128<Half> value);
        public static Vector256<ulong> ConvertToVector256UInt64(Vector128<Half> value);
        public static Vector128<ulong> ConvertToVector128UInt64(Vector128<Half> value);
        public static Vector256<ulong> ConvertToVector256UInt64WithTruncation(Vector128<Half> value);
        public static Vector128<ulong> ConvertToVector128UInt64WithTruncation(Vector128<Half> value);
        public static Vector256<float> ConvertToVector256Single(Vector128<Half> value);
        public static Vector128<float> ConvertToVector128Single(Vector128<Half> value);
        public static Vector256<double> ConvertToVector256Double(Vector128<Half> value);
        public static Vector128<double> ConvertToVector128Double(Vector128<Half> value);
        public static Vector256<Half> Divide(Vector256<Half> left, Vector256<Half> right);
        public static Vector128<Half> Divide(Vector128<Half> left, Vector128<Half> right);
        public static Vector256<Half> FusedMultiplyAdd(Vector256<Half> a, Vector256<Half> b, Vector256<Half> c);
        public static Vector128<Half> FusedMultiplyAdd(Vector128<Half> a, Vector128<Half> b, Vector128<Half> c);
        public static Vector256<Half> FusedMultiplyAddSubtract(Vector256<Half> a, Vector256<Half> b, Vector256<Half> c);
        public static Vector128<Half> FusedMultiplyAddSubtract(Vector128<Half> a, Vector128<Half> b, Vector128<Half> c);
        public static Vector256<Half> FusedMultiplySubtract(Vector256<Half> a, Vector256<Half> b, Vector256<Half> c);
        public static Vector128<Half> FusedMultiplySubtract(Vector128<Half> a, Vector128<Half> b, Vector128<Half> c);
        public static Vector256<Half> FusedMultiplySubtractAdd(Vector256<Half> a, Vector256<Half> b, Vector256<Half> c);
        public static Vector128<Half> FusedMultiplySubtractAdd(Vector128<Half> a, Vector128<Half> b, Vector128<Half> c);
        public static Vector256<Half> FusedMultiplyAddNegated(Vector256<Half> a, Vector256<Half> b, Vector256<Half> c);
        public static Vector128<Half> FusedMultiplyAddNegated(Vector128<Half> a, Vector128<Half> b, Vector128<Half> c);
        public static Vector256<Half> FusedMultiplySubtractNegated(Vector256<Half> a, Vector256<Half> b, Vector256<Half> c);
        public static Vector128<Half> FusedMultiplySubtractNegated(Vector128<Half> a, Vector128<Half> b, Vector128<Half> c);
        public static Vector256<Half> GetExponent(Vector256<Half> value);
        public static Vector128<Half> GetExponent(Vector128<Half> value);
        public static Vector256<Half> GetMantissa(Vector256<Half> value, [ConstantExpected(Max = (byte)(0x0F))] byte control);
        public static Vector128<Half> GetMantissa(Vector128<Half> value, [ConstantExpected(Max = (byte)(0x0F))] byte control);
        public static Vector256<Half> Max(Vector256<Half> left, Vector256<Half> right);
        public static Vector128<Half> Max(Vector128<Half> left, Vector128<Half> right);
        public static Vector256<Half> Min(Vector256<Half> left, Vector256<Half> right);
        public static Vector128<Half> Min(Vector128<Half> left, Vector128<Half> right);
        public static Vector256<Half> Multiply(Vector256<Half> left, Vector256<Half> right);
        public static Vector128<Half> Multiply(Vector128<Half> left, Vector128<Half> right);
        public static Vector256<Half> Reciprocal(Vector256<Half> value);
        public static Vector128<Half> Reciprocal(Vector128<Half> value);
        public static Vector256<Half> ReciprocalSqrt(Vector256<Half> value);
        public static Vector128<Half> ReciprocalSqrt(Vector128<Half> value);
        public static Vector256<Half> RoundScale(Vector256<Half> value, [ConstantExpected] byte control);
        public static Vector128<Half> RoundScale(Vector128<Half> value, [ConstantExpected] byte control);
        public static Vector256<Half> Scale(Vector256<Half> left, Vector256<Half> right);
        public static Vector128<Half> Scale(Vector128<Half> left, Vector128<Half> right);
        public static Vector256<Half> Sqrt(Vector256<Half> value);
        public static Vector128<Half> Sqrt(Vector128<Half> value);
        public static Vector256<Half> Subtract(Vector256<Half> left, Vector256<Half> right);
        public static Vector128<Half> Subtract(Vector128<Half> left, Vector128<Half> right);
        public static Vector256<Half> TernaryLogic(Vector256<Half> a, Vector256<Half> b, Vector256<Half> c, [ConstantExpected] byte control);
        public static Vector128<Half> TernaryLogic(Vector128<Half> a, Vector128<Half> b, Vector128<Half> c, [ConstantExpected] byte control);
        public static Vector256<Half> And(Vector256<Half> left, Vector256<Half> right);
        public static Vector128<Half> And(Vector128<Half> left, Vector128<Half> right);
        public static Vector256<Half> AndNot(Vector256<Half> left, Vector256<Half> right);
        public static Vector128<Half> AndNot(Vector128<Half> left, Vector128<Half> right);
        public static Vector256<Half> BroadcastPairScalarToVector256(Vector128<Half> value);
        public static Vector128<Half> BroadcastPairScalarToVector128(Vector128<Half> value);
        public static Vector256<Half> Or(Vector256<Half> left, Vector256<Half> right);
        public static Vector128<Half> Or(Vector128<Half> left, Vector128<Half> right);
        public static Vector256<Half> Reduce(Vector256<Half> value, [ConstantExpected] byte control);
        public static Vector128<Half> Reduce(Vector128<Half> value, [ConstantExpected] byte control);
        public static Vector256<Half> Xor(Vector256<Half> left, Vector256<Half> right);
        public static Vector128<Half> Xor(Vector128<Half> left, Vector128<Half> right);

    }
}

Addition to AdvSimd.Arm64

Scalar variants are not included in favor of Half's explicit operator optimizations.

namespace System.Runtime.Intrinsics.Arm
{
    public abstract class AdvSimd : ArmBase
    {
        public abstract class Arm64 : ArmBase.Arm64
        {
            public static Vector128<float> ConvertToSingle(Vector64<Half> value);
            public static Vector128<float> ConvertToSingleUpper(Vector128<Half> value);
            public static Vector64<float> ConvertToHalfLower(Vector128<float> value);
            public static Vector128<Half> ConvertToHalfUpper(Vector64<Half> lower, Vector128<float> value);
        }
    }
}

API Sample Usage

F16C

public static void ConverHalfToSingleAvxF16C(Span<float> dst, Span<Half> src)
{
    ref var rdi = ref MemoryMarshal.GetReference(dst);
    ref var rsi = ref MemoryMarshal.GetReference(src);
    nint i, length = Math.Min(src.Length, dst.Length);
    for (i = 0; i < length - 63; i += 64)
    {
        var ymm0 = F16C.ConvertToVector256Single(Unsafe.As<Half, Vector128<Half>>(ref Unsafe.Add(ref rsi, i + 0)));
        var ymm1 = F16C.ConvertToVector256Single(Unsafe.As<Half, Vector128<Half>>(ref Unsafe.Add(ref rsi, i + 8)));
        var ymm2 = F16C.ConvertToVector256Single(Unsafe.As<Half, Vector128<Half>>(ref Unsafe.Add(ref rsi, i + 16)));
        var ymm3 = F16C.ConvertToVector256Single(Unsafe.As<Half, Vector128<Half>>(ref Unsafe.Add(ref rsi, i + 24)));
        Unsafe.As<float, Vector256<float>>(ref Unsafe.Add(ref rdi, i + 0)) = ymm0;
        Unsafe.As<float, Vector256<float>>(ref Unsafe.Add(ref rdi, i + 8)) = ymm1;
        Unsafe.As<float, Vector256<float>>(ref Unsafe.Add(ref rdi, i + 16)) = ymm2;
        Unsafe.As<float, Vector256<float>>(ref Unsafe.Add(ref rdi, i + 24)) = ymm3;
        ymm0 = F16C.ConvertToVector256Single(Unsafe.As<Half, Vector128<Half>>(ref Unsafe.Add(ref rsi, i + 32)));
        ymm1 = F16C.ConvertToVector256Single(Unsafe.As<Half, Vector128<Half>>(ref Unsafe.Add(ref rsi, i + 40)));
        ymm2 = F16C.ConvertToVector256Single(Unsafe.As<Half, Vector128<Half>>(ref Unsafe.Add(ref rsi, i + 48)));
        ymm3 = F16C.ConvertToVector256Single(Unsafe.As<Half, Vector128<Half>>(ref Unsafe.Add(ref rsi, i + 56)));
        Unsafe.As<float, Vector256<float>>(ref Unsafe.Add(ref rdi, i + 32)) = ymm0;
        Unsafe.As<float, Vector256<float>>(ref Unsafe.Add(ref rdi, i + 40)) = ymm1;
        Unsafe.As<float, Vector256<float>>(ref Unsafe.Add(ref rdi, i + 48)) = ymm2;
        Unsafe.As<float, Vector256<float>>(ref Unsafe.Add(ref rdi, i + 56)) = ymm3;
    }
    for (; i < length - 15; i += 16)
    {
        var ymm0 = F16C.ConvertToVector128Single(Unsafe.As<Half, Vector128<Half>>(ref Unsafe.Add(ref rsi, i + 0)));
        var ymm1 = F16C.ConvertToVector128Single(Unsafe.As<Half, Vector128<Half>>(ref Unsafe.Add(ref rsi, i + 8)));
        Unsafe.As<float, Vector256<float>>(ref Unsafe.Add(ref rdi, i + 0)) = ymm0;
        Unsafe.As<float, Vector256<float>>(ref Unsafe.Add(ref rdi, i + 8)) = ymm1;
    }
    for (; i < length; i++)
    {
        Unsafe.Add(ref rdi, i) = (float)Unsafe.Add(ref rsi, i);
    }
}

AdvSimd.Arm64

public static void ConverHalfToSingleAdvSimd64(Span<float> dst, Span<Half> src)
{
    ref var x9 = ref MemoryMarshal.GetReference(dst);
    ref var x10 = ref MemoryMarshal.GetReference(src);
    nint i, length = Math.Min(src.Length, dst.Length);
    for (i = 0; i < length - 31; i += 32)
    {
        ref var x11 = ref Unsafe.As(ref x9, i);
        var v1_8h = Unsafe.As<Half, Vector128<Half>>(ref Unsafe.Add(ref x10, i + 0));
        var v3_8h = Unsafe.As<Half, Vector128<Half>>(ref Unsafe.Add(ref x10, i + 8));
        var v0_4s = AdvSimd.Arm64.ConvertToSingle(v1_8h.GetLower());
        var v2_4s = AdvSimd.Arm64.ConvertToSingle(v3_8h.GetLower());
        var v1_4s = AdvSimd.Arm64.ConvertToSingleUpper(v1_8h);
        var v3_4s = AdvSimd.Arm64.ConvertToSingleUpper(v3_8h);
        var v5_8h = Unsafe.As<Half, Vector128<Half>>(ref Unsafe.Add(ref x10, i + 16));
        var v7_8h = Unsafe.As<Half, Vector128<Half>>(ref Unsafe.Add(ref x10, i + 24));
        Unsafe.As<float, Vector128<float>>(ref Unsafe.Add(ref x11, 0)) = v0_4s;
        Unsafe.As<float, Vector128<float>>(ref Unsafe.Add(ref x11, 8)) = v2_4s;
        var v4_4s = AdvSimd.Arm64.ConvertToSingle(v5_8h.GetLower());
        var v6_4s = AdvSimd.Arm64.ConvertToSingle(v7_8h.GetLower());
        Unsafe.As<float, Vector128<float>>(ref Unsafe.Add(ref x11, 4)) = v1_4s;
        Unsafe.As<float, Vector128<float>>(ref Unsafe.Add(ref x11, 12)) = v3_4s;
        var v5_4s = AdvSimd.Arm64.ConvertToSingleUpper(v5_8h);
        var v7_4s = AdvSimd.Arm64.ConvertToSingleUpper(v7_8h);
        Unsafe.As<float, Vector128<float>>(ref Unsafe.Add(ref x11, 16)) = v4_4s;
        Unsafe.As<float, Vector128<float>>(ref Unsafe.Add(ref x11, 24)) = v6_4s;
        Unsafe.As<float, Vector128<float>>(ref Unsafe.Add(ref x11, 20)) = v5_4s;
        Unsafe.As<float, Vector128<float>>(ref Unsafe.Add(ref x11, 28)) = v7_4s;
    }
    for (i = 0; i < length - 7; i += 8)
    {
        var v1_8h = Unsafe.As<Half, Vector128<Half>>(ref Unsafe.Add(ref x10, i));
        ref var x11 = ref Unsafe.As(ref x9, i);
        var v0_4s = AdvSimd.Arm64.ConvertToSingle(v1_8h.GetLower());
        var v1_4s = AdvSimd.Arm64.ConvertToSingleUpper(v1_8h);
        Unsafe.As<float, Vector128<float>>(ref Unsafe.Add(ref x11, 0)) = v0_4s;
        Unsafe.As<float, Vector128<float>>(ref Unsafe.Add(ref x11, 4)) = v1_4s;
    }
    for (; i < length; i++)
    {
        Unsafe.Add(ref x9, i) = (float)Unsafe.Add(ref x10, i);
    }
}

Alternative Designs

No response

Risks

No response

ghost commented 2 years ago

Tagging subscribers to this area: @dotnet/area-system-numerics See info in area-owners.md if you want to be subscribed.

Issue Details
### Background and motivation In Ivy Bridge and newer x86 processors, F16C is provided to convert between `float` and `Half`. So I think it's great if .NET had F16C support. ### API Proposal If you want to avoid using `Vector64`: ```C# namespace System.Runtime.Intrinsics.X86 { public abstract class F16C : Avx { public static bool IsSupported { get; } public static Vector128 ConvertToVector128Single(Vector128 value); public static Vector256 ConvertToVector256Single(Vector128 value); public static Vector128 ConvertToVector128Half(Vector128 value); public static Vector128 ConvertToVector128Half(Vector256 value); public static Vector128 ConvertToVector128HalfRoundToEven(Vector128 value); public static Vector128 ConvertToVector128HalfRoundToEven(Vector256 value); public static Vector128 ConvertToVector128HalfRoundToZero(Vector128 value); public static Vector128 ConvertToVector128HalfRoundToZero(Vector256 value); public static Vector128 ConvertToVector128HalfRoundToNegativeInfinity(Vector128 value); public static Vector128 ConvertToVector128HalfRoundToNegativeInfinity(Vector256 value); public static Vector128 ConvertToVector128HalfRoundToPositiveInfinity(Vector128 value); public static Vector128 ConvertToVector128HalfRoundToPositiveInfinity(Vector256 value); } } ``` If using `Vector64` is welcome: ```C# namespace System.Runtime.Intrinsics.X86 { public abstract class F16C : Avx { public static bool IsSupported { get; } public static Vector128 ConvertToVector128Single(Vector64 value); public static Vector256 ConvertToVector256Single(Vector128 value); public static Vector64 ConvertToVector64Half(Vector128 value); public static Vector128 ConvertToVector128Half(Vector256 value); public static Vector64 ConvertToVector64HalfRoundToEven(Vector128 value); public static Vector128 ConvertToVector128HalfRoundToEven(Vector256 value); public static Vector64 ConvertToVector64HalfRoundToZero(Vector128 value); public static Vector128 ConvertToVector128HalfRoundToZero(Vector256 value); public static Vector64 ConvertToVector64HalfRoundToNegativeInfinity(Vector128 value); public static Vector128 ConvertToVector128HalfRoundToNegativeInfinity(Vector256 value); public static Vector64 ConvertToVector64HalfRoundToPositiveInfinity(Vector128 value); public static Vector128 ConvertToVector128HalfRoundToPositiveInfinity(Vector256 value); } } ``` And also `Vector(64|128|256)` have to add: ```C# namespace System.Runtime.Intrinsics { public static class Vector64 { public static Vector64 AsHalf(this Vector64 vector) where T : struct; } public static class Vector128 { public static Vector128 AsHalf(this Vector128 vector) where T : struct; } public static class Vector256 { public static Vector256 AsHalf(this Vector256 vector) where T : struct; } } ``` and `Vector(64|128|256)` shouldn't throw any exceptions if `T` was `Half`. ### API Usage This code is based on `Vector64` design. ```C# public static void ConverHalfToSingle(Span dst, Span src) { ref var rdi = ref MemoryMarshal.GetReference(dst); ref var rsi = ref MemoryMarshal.GetReference(src); nint i, length = buffer.Length; for (i = 0; i < length - 63; i += 64) { var ymm0 = F16C.ConvertToVector256Single(Unsafe.As>(ref Unsafe.Add(ref rsi, i + 0))); var ymm1 = F16C.ConvertToVector256Single(Unsafe.As>(ref Unsafe.Add(ref rsi, i + 8))); var ymm2 = F16C.ConvertToVector256Single(Unsafe.As>(ref Unsafe.Add(ref rsi, i + 16))); var ymm3 = F16C.ConvertToVector256Single(Unsafe.As>(ref Unsafe.Add(ref rsi, i + 24))); Unsafe.As>(ref Unsafe.Add(ref rdi, i + 0)) = ymm0; Unsafe.As>(ref Unsafe.Add(ref rdi, i + 8)) = ymm1; Unsafe.As>(ref Unsafe.Add(ref rdi, i + 16)) = ymm2; Unsafe.As>(ref Unsafe.Add(ref rdi, i + 24)) = ymm3; ymm0 = F16C.ConvertToVector256Single(Unsafe.As>(ref Unsafe.Add(ref rsi, i + 32))); ymm1 = F16C.ConvertToVector256Single(Unsafe.As>(ref Unsafe.Add(ref rsi, i + 40))); ymm2 = F16C.ConvertToVector256Single(Unsafe.As>(ref Unsafe.Add(ref rsi, i + 48))); ymm3 = F16C.ConvertToVector256Single(Unsafe.As>(ref Unsafe.Add(ref rsi, i + 56))); Unsafe.As>(ref Unsafe.Add(ref rdi, i + 32)) = ymm0; Unsafe.As>(ref Unsafe.Add(ref rdi, i + 40)) = ymm1; Unsafe.As>(ref Unsafe.Add(ref rdi, i + 48)) = ymm2; Unsafe.As>(ref Unsafe.Add(ref rdi, i + 56)) = ymm3; } for (; i < length - 7; i += 8) { var xmm0 = F16C.ConvertToVector128Single(Unsafe.As>(ref Unsafe.Add(ref rsi, i + 0))); var xmm1 = F16C.ConvertToVector128Single(Unsafe.As>(ref Unsafe.Add(ref rsi, i + 4))); Unsafe.As>(ref Unsafe.Add(ref rdi, i + 0)) = xmm0; Unsafe.As>(ref Unsafe.Add(ref rdi, i + 4)) = xmm1; } for (; i < length; i++) { Unsafe.Add(ref rdi, i) = (float)Unsafe.Add(ref rsi, i); } } ``` ### Alternative Designs _No response_ ### Risks _No response_
Author: MineCake147E
Assignees: -
Labels: `api-suggestion`, `area-System.Numerics`, `untriaged`
Milestone: -
ghost commented 2 years ago

Tagging subscribers to this area: @dotnet/area-system-runtime-intrinsics See info in area-owners.md if you want to be subscribed.

Issue Details
### Background and motivation In Ivy Bridge and newer x86 processors, F16C is provided to convert between `float` and `Half`. So I think it's great if .NET had F16C support. ### API Proposal If you want to avoid using `Vector64`: ```C# namespace System.Runtime.Intrinsics.X86 { public abstract class F16C : Avx { public static bool IsSupported { get; } public static Vector128 ConvertToVector128Single(Vector128 value); public static Vector256 ConvertToVector256Single(Vector128 value); public static Vector128 ConvertToVector128Half(Vector128 value); public static Vector128 ConvertToVector128Half(Vector256 value); public static Vector128 ConvertToVector128HalfRoundToEven(Vector128 value); public static Vector128 ConvertToVector128HalfRoundToEven(Vector256 value); public static Vector128 ConvertToVector128HalfRoundToZero(Vector128 value); public static Vector128 ConvertToVector128HalfRoundToZero(Vector256 value); public static Vector128 ConvertToVector128HalfRoundToNegativeInfinity(Vector128 value); public static Vector128 ConvertToVector128HalfRoundToNegativeInfinity(Vector256 value); public static Vector128 ConvertToVector128HalfRoundToPositiveInfinity(Vector128 value); public static Vector128 ConvertToVector128HalfRoundToPositiveInfinity(Vector256 value); } } ``` If using `Vector64` is welcome: ```C# namespace System.Runtime.Intrinsics.X86 { public abstract class F16C : Avx { public static bool IsSupported { get; } public static Vector128 ConvertToVector128Single(Vector64 value); public static Vector256 ConvertToVector256Single(Vector128 value); public static Vector64 ConvertToVector64Half(Vector128 value); public static Vector128 ConvertToVector128Half(Vector256 value); public static Vector64 ConvertToVector64HalfRoundToEven(Vector128 value); public static Vector128 ConvertToVector128HalfRoundToEven(Vector256 value); public static Vector64 ConvertToVector64HalfRoundToZero(Vector128 value); public static Vector128 ConvertToVector128HalfRoundToZero(Vector256 value); public static Vector64 ConvertToVector64HalfRoundToNegativeInfinity(Vector128 value); public static Vector128 ConvertToVector128HalfRoundToNegativeInfinity(Vector256 value); public static Vector64 ConvertToVector64HalfRoundToPositiveInfinity(Vector128 value); public static Vector128 ConvertToVector128HalfRoundToPositiveInfinity(Vector256 value); } } ``` And also `Vector(64|128|256)` have to add: ```C# namespace System.Runtime.Intrinsics { public static class Vector64 { public static Vector64 AsHalf(this Vector64 vector) where T : struct; } public static class Vector128 { public static Vector128 AsHalf(this Vector128 vector) where T : struct; } public static class Vector256 { public static Vector256 AsHalf(this Vector256 vector) where T : struct; } } ``` and `Vector(64|128|256)` shouldn't throw any exceptions if `T` was `Half`. ### API Usage This code is based on `Vector64` design. ```C# public static void ConverHalfToSingle(Span dst, Span src) { ref var rdi = ref MemoryMarshal.GetReference(dst); ref var rsi = ref MemoryMarshal.GetReference(src); nint i, length = buffer.Length; for (i = 0; i < length - 63; i += 64) { var ymm0 = F16C.ConvertToVector256Single(Unsafe.As>(ref Unsafe.Add(ref rsi, i + 0))); var ymm1 = F16C.ConvertToVector256Single(Unsafe.As>(ref Unsafe.Add(ref rsi, i + 8))); var ymm2 = F16C.ConvertToVector256Single(Unsafe.As>(ref Unsafe.Add(ref rsi, i + 16))); var ymm3 = F16C.ConvertToVector256Single(Unsafe.As>(ref Unsafe.Add(ref rsi, i + 24))); Unsafe.As>(ref Unsafe.Add(ref rdi, i + 0)) = ymm0; Unsafe.As>(ref Unsafe.Add(ref rdi, i + 8)) = ymm1; Unsafe.As>(ref Unsafe.Add(ref rdi, i + 16)) = ymm2; Unsafe.As>(ref Unsafe.Add(ref rdi, i + 24)) = ymm3; ymm0 = F16C.ConvertToVector256Single(Unsafe.As>(ref Unsafe.Add(ref rsi, i + 32))); ymm1 = F16C.ConvertToVector256Single(Unsafe.As>(ref Unsafe.Add(ref rsi, i + 40))); ymm2 = F16C.ConvertToVector256Single(Unsafe.As>(ref Unsafe.Add(ref rsi, i + 48))); ymm3 = F16C.ConvertToVector256Single(Unsafe.As>(ref Unsafe.Add(ref rsi, i + 56))); Unsafe.As>(ref Unsafe.Add(ref rdi, i + 32)) = ymm0; Unsafe.As>(ref Unsafe.Add(ref rdi, i + 40)) = ymm1; Unsafe.As>(ref Unsafe.Add(ref rdi, i + 48)) = ymm2; Unsafe.As>(ref Unsafe.Add(ref rdi, i + 56)) = ymm3; } for (; i < length - 7; i += 8) { var xmm0 = F16C.ConvertToVector128Single(Unsafe.As>(ref Unsafe.Add(ref rsi, i + 0))); var xmm1 = F16C.ConvertToVector128Single(Unsafe.As>(ref Unsafe.Add(ref rsi, i + 4))); Unsafe.As>(ref Unsafe.Add(ref rdi, i + 0)) = xmm0; Unsafe.As>(ref Unsafe.Add(ref rdi, i + 4)) = xmm1; } for (; i < length; i++) { Unsafe.Add(ref rdi, i) = (float)Unsafe.Add(ref rsi, i); } } ``` ### Alternative Designs _No response_ ### Risks _No response_
Author: MineCake147E
Assignees: -
Labels: `api-suggestion`, `area-System.Numerics`, `area-System.Runtime.Intrinsics`, `untriaged`
Milestone: -
MineCake147E commented 2 years ago

I have edited my comments, including adding Intrinsics for ARM.

MichalPetryka commented 1 year ago

Worth noting that now with AVX512-FP16 all operations are now accelerated on XArch.

rickbrew commented 1 year ago

I have an upcoming need for VectorXXX<Half>, specifically for conversions between Float32 and Float16.

I'm adding color management to my image editing app (Paint.NET). Storing the canvas tiles at a higher precision (RGBA Float16 instead of BGRA byte aka BGRA32) will enable me to maintain higher color accuracy throughout my rendering pipeline. These tiles are often mipmaps for when the user has zoomed out (e.g. 50% or lower), which involves resizing. I do all of the mipmap generation on the CPU and then copy the tile bitmaps to the GPU (for Direct2D). (If I did mipmap generation on the GPU, it would consume an extraordinary amount of GPU memory and flood the PCI-E bus. It doesn't really work.)

The resizing and color transform steps are done on the CPU at Float32 precision with very high performance thanks to @saucecontrol's PhotoSauce.MagicScaler library. I then convert from RGBA Float32 back to BGRA32 on the CPU, which later on is then presented to the screen via Direct2D, converting to sRGB/scRGB with the Color Management effect, which operates at high precision (up to Float32) and renders into a Float16 swapchain.

So the conversion goes from BGRA32 (CPU bitmap) --> RGBA Float32 (CPU intermediate buffers) -> BGRA32 (D2D bitmap) -> RGBA Float32 (effect intermediate texture(s)) -> RGBA Float16 (render target / swapchain).

I'd like to be able to do BGRA32 (CPU bitmap) --> RGBA Float32 (CPU intermediate buffers) -> RGBA Float16 (D2D bitmap) -> RGBA Float32 (effect intermediate texture(s)) -> RGBA Float16 (render target / swapchain).

I can use RGBA Float32 textures on the GPU today, but Float16 uses 1/2 the memory and PCI-E bandwidth, and won't lose any useful precision at this point in the rendering pipeline.

For now I can P/Invoke a method in a native C/C++ DLL, but having native support in C# would be great.

MineCake147E commented 11 months ago

Added proposal of AVX-512 FP16 APIs.