System.Numerics.Vectors 'Vector<T>': 它基本上只是 System.UInt128 吗?

System.Numerics.Vectors 'Vector<T>': is it basically just System.UInt128?

我正在调查 Vector<T> in the System.Numerics.Vectors namespace from version 4.5.0-preview1-26216-02。 MSDN 文档说:

Vector<T> is an immutable structure that represents a single vector of a specified numeric type. The count of a Vector<T> instance is fixed, but its upper limit is CPU-register dependent.
https://docs.microsoft.com/en-us/dotnet/api/system.numerics.vector-1 (emphasis added)

即使忽略了错误的措辞“count [sic.] of a Vector”,这句话似乎也很不清楚,因为它暗示不同的 Vector<T> 实例可能有不同——尽管 "fixed" 达到一些 CPU-limit--"counts"(再次,所谓的 'count' 什么,确切地说?)这里没有提到实际的 Count 属性——或者实际上 intro page 上的任何地方)。

现在一般来说,我觉得"read-only"或者"immutable"比[=88=更常用] 用于描述实例的属性或字段,但在这种情况下,事实证明 Vector<T>.Count 属性 虽然确实是只读的,但也是 static,因此与任何 Vector<T> 实例 没有任何关联。相反,它的值仅根据泛型类型参数 T 变化(然后可能是从机器到机器,如图所示):

bool hw = Vector.IsHardwareAccelerated;    // --> true

var c = (Vector<sbyte>.Count, Vector<short>.Count, Vector<int>.Count, Vector<long>.Count);
Debug.WriteLine(c);    // -->  (16, 8, 4, 2)

哦。

那么它基本上是 System.Int128 伪装的吗?是这样吗?我的问题是:

矢量大小并不总是 16 字节,尽管这很常见。例如在具有 AVX2, programs run in 64bit mode get 32 byte vectors. In this way the Count property can also vary on the same machine (for the same T), by running the program in different modes. In principle it wouldn't have to be like that, a 32-bit program could still use 256-bit operations even with just AVX1 support, but that's not how System.Numerics.Vectors works. The varying size per feature-level of the CPU is a fairly fundamental part of the design of the API, presumably to enable some form of future-proofing, though it perhaps contributed to the lack of shuffles 的平台上(对于 not-statically-known 大小的向量很难指定)。

I thought this library would allow the use of much wider hardware-accelerated datatypes than just 128 bits

硬件中不存在,因此很难提供。 AVX-512 goes up to 512 bits as the name implies, but that's as far as SIMD 主流 CPU 现在可以了。

why not just call it System.Int128 / System.UInt128

我希望这些类型映射到实际的整数类型,而不是向量类型。许多对 128 位整数有意义的操作实际上并不存在 CPU 指令,几乎所有 do 存在的操作都在 2 × 上运行64 (Vector<long>, long[2]), 4 × 32 (Vector<int>, int[4]), 8 × 16 (Vector<short>, short[8]) 或 16 × 8 (Vector<byte>, byte[16])位向量(或在支持它的平台上加倍宽度)。在 Int128 上提供 "byte-wise add" 操作会很奇怪,而 而不是 提供真正的 128 位加法使它更奇怪。除了前面提到的,大小不是定义为 128 位,这很常见。

许多 SIMD 操作都非常快,但也有一些例外。例如,32 位乘法通常具有相当极端的延迟。 System.Numerics.Vectors API 还允许一些 non-existent 操作(必须慢慢模拟,例如整数除法或字节乘法)而不暗示存在问题。映射到实际存在的指令的操作大多很快。

虽然 ulong 上的按位运算也很快,但从 "total work done per unit time." 的角度来看,它们的矢量版本甚至更好 例如,Skylake 可以执行(最多)四个标量每个周期的按位操作(但额外的操作,如加法和 compare/branch 进行循环会竞争相同的资源)但使用 SIMD 执行三个 256 位按位操作,这是同时工作量的 3 倍并为标量操作或分支保留一个执行端口。

所以是的,它可能值得使用。您可以保留 ulong 的数组并使用 Vector<T>construct-from-array 构造函数,这样您就不必到处处理向量。例如,使用可变索引对向量进行索引根本不是一个好的操作,会导致分支、向量存储和标量重新加载。向量的 variable-size 性质显然也使直接使用它们的数组变得非常复杂,而不是使用原始类型数组然后从它们中 vector-loading 。不过,您可以轻松地将数组的长度四舍五入为向量计数的倍数,从而无需使用小标量循环来处理数组末尾不太适合向量的剩余项。