Skip to content

API proposal: Enable AsVector<T> in Vector<T> #508

@msedi

Description

@msedi

Vector<T> is a helpful tool to improve performance of certain code parts without large efforts.
For number crunching we are using the Vector<T> very often, since our data is pretty large and the application of Vector<T> really boosts performance. Also when it comes to data conversion or data reinterpretation Vector<T> is an essential part and has some methods to perform also these tasks.

Rationale and Usage

However, the AsVector"T" in System.Numerics.Vector can only be used comfortably when explicitly using their names for the given data type. The problem is that when dealing with a "generic" environment where the datatypes are not known in advance and can change often it is currently hard to use the above method. The same holds true for the 'ConvertTo"T"' routines that could als be expressed with the API suggestion below.

Following example illustrates the problem:

// Cast the values from byte to "arbitrary" type.
// Assume the length of bytes are an integer multiple of data type size of the result.
public unsafe static T[] ReadArray<T>(byte[] data) where T : unmanaged
{
    int Lsource = Vector<byte>.Count;
    int Ltarget = Vector<T>.Count;

    int N = data.Length % data.Length;
    T[] result = new T[data.Length / sizeof(T)];
    int isource = 0;
    int itarget = 0;

    for (; isource < N; isource += Lsource, itarget += Ltarget)
    {
        if (typeof(T) == typeof(byte))
            Vector.AsVectorByte(new Vector<byte>(data, isource)).CopyTo((byte[])(object)result, itarget);
        else if (typeof(T) == typeof(sbyte))
            Vector.AsVectorSByte(new Vector<byte>(data, isource)).CopyTo((sbyte[])(object)result, itarget);
        else if (typeof(T) == typeof(short))
            Vector.AsVectorInt16(new Vector<byte>(data, isource)).CopyTo((short[])(object)result, itarget);
        else if (typeof(T) == typeof(ushort))
            Vector.AsVectorUInt16(new Vector<byte>(data, isource)).CopyTo((ushort[])(object)result, itarget);
        else if (typeof(T) == typeof(int))
            Vector.AsVectorInt32(new Vector<byte>(data, isource)).CopyTo((int[])(object)result, itarget);
        else if (typeof(T) == typeof(uint))
            Vector.AsVectorUInt32(new Vector<byte>(data, isource)).CopyTo((uint[])(object)result, itarget);
        else if (typeof(T) == typeof(long))
            Vector.AsVectorInt64(new Vector<byte>(data, isource)).CopyTo((long[])(object)result, itarget);
        else if (typeof(T) == typeof(ulong))
            Vector.AsVectorUInt64(new Vector<byte>(data, isource)).CopyTo((ulong[])(object)result, itarget);
        else if (typeof(T) == typeof(float))
            Vector.AsVectorSingle(new Vector<byte>(data, isource)).CopyTo((float[])(object)result, itarget);
        else if (typeof(T) == typeof(double))
            Vector.AsVectorDouble(new Vector<byte>(data, isource)).CopyTo((double[])(object)result, itarget);
        else
            throw new NotSupportedException();
    }

    return result;
}

Proposed API

The proposed API for this specific case is only a hand-crafted if..else solution as a wrapper around the current interface, but would solve problems in methods where the input and output datatypes can be better expressed by using generics. Following method is an API proposal that solves the problem for generic environments.

namespace System.Numerics
{
    public partial class Vector
    {
        public static Vector<TTo> As<TFrom, TTo>(Vector<TFrom> vector)
            where TFrom : struct
            where TTo : struct;
    }
}

Original Proposal

AsVector

public unsafe static Vector<Tout> AsVector<Tin, Tout>(Tin[] data) where Tin : struct where Tout : struct
{
    if (typeof(Tout) == typeof(byte))
        return (Vector<Tout>)(object)Vector.AsVectorByte(new Vector<Tin>(data));
    else if (typeof(Tout) == typeof(sbyte))
        return (Vector<Tout>)(object)Vector.AsVectorSByte(new Vector<Tin>(data));
    else if (typeof(Tout) == typeof(Tin))
        return (Vector<Tout>)(object)Vector.AsVectorInt16(new Vector<Tin>(data));
    else if (typeof(Tout) == typeof(ushort))
        return (Vector<Tout>)(object)Vector.AsVectorUInt16(new Vector<Tin>(data));
    else if (typeof(Tout) == typeof(int))
        return (Vector<Tout>)(object)Vector.AsVectorInt32(new Vector<Tin>(data));
    else if (typeof(Tout) == typeof(uint))
        return (Vector<Tout>)(object)Vector.AsVectorUInt32(new Vector<Tin>(data));
    else if (typeof(Tout) == typeof(long))
        return (Vector<Tout>)(object)Vector.AsVectorInt64(new Vector<Tin>(data));
    else if (typeof(Tout) == typeof(ulong))
        return (Vector<Tout>)(object)Vector.AsVectorUInt64(new Vector<Tin>(data));
    else if (typeof(Tout) == typeof(float))
        return (Vector<Tout>)(object)Vector.AsVectorSingle(new Vector<Tin>(data));
    else if (typeof(Tout) == typeof(double))
        return (Vector<Tout>)(object)Vector.AsVectorDouble(new Vector<Tin>(data));
    else
        throw new NotSupportedException();
}

ConvertTo

When writing the proposal for ´ConvertTo` I encountered a problem where ConvertTo only supports a certain range of data types. I assume that this is due the support of SSE only. The question here is if it can be extended to all datatypes or at least have some sort of fallback that can deal with other datatypes.

public unsafe static Vector<Tout> ConvertTo<Tin, Tout>(Tin[] data) where Tin : struct where Tout : struct
{
    if (typeof(Tout) == typeof(byte))
        return (Vector<Tout>)(object)Vector.ConvertToByte(new Vector<Tin>(data));
    else if (typeof(Tout) == typeof(sbyte))
        return (Vector<Tout>)(object)Vector.ConvertToSByte(new Vector<Tin>(data));
    else if (typeof(Tout) == typeof(Tin))
        return (Vector<Tout>)(object)Vector.ConvertToInt16(new Vector<Tin>(data));
    else if (typeof(Tout) == typeof(ushort))
        return (Vector<Tout>)(object)Vector.AsVectorUInt16(new Vector<Tin>(data));
    else if (typeof(Tout) == typeof(int))
        return (Vector<Tout>)(object)Vector.AsVectorInt32(new Vector<Tin>(data));
    else if (typeof(Tout) == typeof(uint))
        return (Vector<Tout>)(object)Vector.AsVectorUInt32(new Vector<Tin>(data));
    else if (typeof(Tout) == typeof(long))
        return (Vector<Tout>)(object)Vector.AsVectorInt64(new Vector<Tin>(data));
    else if (typeof(Tout) == typeof(ulong))
        return (Vector<Tout>)(object)Vector.ConvertToUInt64(new Vector<Tin>(data));
    else if (typeof(Tout) == typeof(float))
        return (Vector<Tout>)(object)Vector.ConvertToSingle(new Vector<Tin>(data));
    else if (typeof(Tout) == typeof(double))
        return (Vector<Tout>)(object)Vector.ConvertToDouble(new Vector<Tin>(data));
    else
        throw new NotSupportedException();
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    Cost:SWork that requires one engineer up to 1 weekapi-approvedAPI was approved in API review, it can be implementedarea-System.Numericshelp wanted[up-for-grabs] Good issue for external contributors

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions