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

时间:2018-04-05 00:11:02

标签: .net bit-manipulation simd hardware-acceleration

我正在查看版本Vector<T> System.Numerics.Vectors 命名空间中的4.5.0-preview1-26216-02。 MSDN文档说:

  

Vector<T>是一个不可变结构,表示指定数字类型的单个向量。 Vector<T>实例的计数已修复 ,但其上限取决于CPU注册。
https://docs.microsoft.com/en-us/dotnet/api/system.numerics.vector-1(强调补充)

即使忽略误导的措辞“计算 [sic。] of vector ”,这句话似乎也很不清楚,因为它意味着不同的Vector<T>个实例可能有不同的 - 虽然“固定”到一些CPU限制 - “计数”(再次,所谓的“计数”,究竟是什么?)这里没有提到实际的Count属性 - 或者事实上intro page)上的任何地方。

现在通常情况下,我认为“只读”“不可变”比传统上用于描述实例属性或字段的“固定”更常用,但在此结果表明,Vector<T>.Count属性虽然确实是只读的,但也是 静态 ,因此无法与任何{{1}相关联} 实例。相反,它的值仅根据泛型类型参数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,但我认为这个库允许使用 很多 更广泛的硬件加速数据类型不仅仅是128位。我的HPSG解析引擎通常执行5,000+位的密集位计算向量。
  • 再次假设我没有忽略这一点,为什么不称它为SIMD / System.Int128而不是System.UInt128?使用通用基元类型对其进行参数化确实带来了一定的好处,但是我错误地认为它更像是一个有用的扩展数组(即,blittable elements Vector<T>),而不是仅仅是一个双倍宽度的CPU注册,在我看来,就像你可以得到的那样是“标量”。

    不要误解我的意思,128位寄存器是有趣的,有用的,令人兴奋的东西 - 如果这里只是有点超卖?例如,T无论是什么,都会有16个元素,无论你是否需要或者全部使用它们,所以Vector<byte>的精神在运行时期望因实例而异,似乎被误用了这里。

  • 即使单个Count不能直接处理我所描述的用例,我也希望更新我当前的实现(使用Vector<T>数组对于每个N位向量,而是使用ulong[N >> 6]数组?

      

    ...是的,那是“ Vector<ulong>[N >> 7]的数组”,这对我来说似乎很奇怪;不应该在名称中使用“Vector”的类型充分或有用地扩展,而不必显式创建一个数组来包装多个实例吗?

  • 除了每个128位SIMD按位操作处理两倍数据的事实之外,每个操作码的周期内SIMD逐位运算还会更快(或更慢)吗?
  • 目前是否有其他常用或可用的硬件平台Vector<ulong>实际报告的SIMD位宽不同?

1 个答案:

答案 0 :(得分:8)

矢量大小并不总是16个字节,尽管这很常见。例如,在具有AVX2的平台上,以64位模式运行的程序获得32字节向量。通过这种方式,Count属性也可以在同一台机器上变化(对于相同的T),通过在不同模式下运行程序。原则上它不一定是这样,即使只支持AVX1,32位程序仍然可以使用256位操作,但这不是System.Numerics.Vectors的工作原理。 CPU的每个功能级别的不同大小是API设计的一个相当基本的部分,可能是为了实现某种形式的面向未来,尽管它可能导致shuffles的缺乏(这很难)指定非静态已知大小的矢量。

  

我认为这个库允许使用比128位

更宽的硬件加速数据类型

硬件中不存在,因此很难提供。顾名思义,AVX-512最多可达512位,但目前主流CPU的SIMD仍然存在。

  

为什么不称它为System.Int128 / System.UInt128

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

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

虽然System.Numerics.Vectors上的按位操作也很快,但从“每单位时间完成的总工作量”来看,它们的矢量版本甚至更好。例如,Skylake每个周期可以执行(最多)四个标量按位运算(但是额外的操作,如加法和比较/分支,以使循环在同一资源上竞争),但执行三个256位按位运算SIMD是同时工作量的3倍,它为标量操作或分支留下了执行端口。

所以是的,它可能值得使用。您可以保留ulong数组并使用ulong的{​​{3}}构造函数,这样您就不必处理任何地方的向量。例如,索引到具有变量索引的向量根本不是一个好的操作,导致分支,向量存储和标量重新加载。矢量的可变大小性质显然也使得直接使用它们的数组变得非常复杂,而不是使用基元类型的数组然后从它们进行向量加载。您可以轻松地将数组的长度向上舍入到向量计数的倍数,以消除对小数组循环的需要,以处理不完全适合向量的数组末尾的剩余项目。 / p>