如何解决您可以将泛型传递给 .NET Core 硬件内部函数吗?
我正在编写一个基本库来试验 C# 硬件内在函数(System.Runtime.Intrinsics*
命名空间),并且有一个方法可以支持任何“硬件”类型(Byte
、SByte
.. . 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
约束允许额外的非“硬件”类型(Decimal
、enum
等),因此限制性不足以保证适当的重载会存在。
定义一个接口作为与 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
获取向量的容量。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。