您可以将泛型传递给 .NET Core 硬件内部函数吗?

问题描述

我正在编写一个基本库来试验 C# 硬件内在函数(System.Runtime.Intrinsics* 命名空间),并且有一个方法可以支持任何“硬件”类型(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 约束允许额外的非“硬件”类型(Decimalenum 等),因此限制性不足以保证适当的重载会存在。

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

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

解决方法

一般来说,你不能为泛型做到这一点。至少因为 Vectors 没有通用的 create 方法或 cast 选项。但是 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 代码的朋友。至少因为它必须尽可能快,但无论是 switch 还是 if,甚至 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 获取向量的容量。

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...