可以将泛型传递给 .NET Core 硬件内在函数方法吗?

Can you pass generics to .NET Core hardware intrinsics methods?

我正在编写一个基本库来试验 C# 硬件内在函数(System.Runtime.Intrinsics* 命名空间),并且有一个方法可以支持任何 'hardware' 类型(ByteSByte ... UInt64, Double)

当尝试使用泛型签名时,编译器无法使用泛型并且无法选择正确的重载;例如:

public static unsafe void GenericSimd<T>(T value, ReadOnlySpan<T> span) where T : unmanaged
{
    fixed (T* fixedSpan = span)
    {
        Vector128<T> vec0 = Vector128.Create(value);       // CS1503, Cannot convert T to byte
        Vector128<T> vec1 = Sse2.LoadVector128(fixedSpan); // CS1503, Cannot convert T* to byte*
    }
}

参考:CS1503

我认为这是由于 unmanaged 约束允许额外的非 'hardware' 类型(Decimalenum 等),因此限制不够保证存在适当的过载。

定义一个接口作为附加约束与 unmanaged 一起使用也是行不通的,因为它需要部分内置类型。

有没有办法使用泛型实现此方法并避免为每种类型编写重载?

一般来说,您不能对泛型执行此操作。至少因为 Vectors 没有通用的创建方法或转换选项。但是 Span<T>.

有一个选项
public static unsafe void GenericSimd<T>(ReadOnlySpan<T> span) 
    where T : struct
{
    ReadOnlySpan<byte> bytes = MemoryMarshal.Cast<T, byte>(span); // no data copy here involved, it's lightning fast
    fixed (byte* fixedSpan = bytes)
    {
        // this way
        Vector128<byte> vec1 = *(Vector128<byte>*)fixedSpan;
        // or this way
        Vector128<byte> vec2 = Sse2.LoadVector128(fixedSpan);
    }
}

但请确保您在 Span 中有足够的字节(16 或更多)来填充完整的 Vector128<byte>

你也可能得到 T

的尺码
int size = Marshal.SizeOf(typeof(T));

然后 switch-case 取决于变量的大小。但是处理整数和浮点数所需数据的行为不同。

很多切换逻辑不适合 SSE/AVX 代码。至少因为它必须尽可能快,但是 switchif,即使 Cast 也会消耗 CPU 资源。

我建议您制作类似于 .NET SSE/AVX 方法的非泛型重载。

顺便说一句,如果您需要纯通用硬件加速 Vector<T> - welcome to System.Numerics.Vectors。我测试过,在大多数情况下,它在我的 Core i7 上显示出与 Intrinsics 相同的性能。

public static void GenericSimd<T>(T value, ReadOnlySpan<T> span)
    where T : struct
{
    Vector<T> vector1 = new Vector<T>(value); // fine
    Vector<T> vector2 = new Vector<T>(span); // also fine
}

您也可以检查例如Vector<int>.Count 获取向量的容量。