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

问题描述 投票:1回答:1

我正在从版本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。]”,这句话似乎还不清楚,因为它暗示不同的Vector<T>实例可能有不同 - 尽管“固定”到某些CPU限制 - “计数”(再一次,究竟是什么'所谓的'计数'?)这里没有提到一个真正的Count财产 - 或者实际上在intro page的任何地方。

现在通常情况下,我认为“只读”或“不可变”在描述实例的属性或字段方面比传统方法更常用,但在这种情况下,事实证明Vector<T>.Count属性虽然也是只读的,但也是静态,因此与任何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?是吗?我的问题是:

  • 我错过了什么吗?我知道这是真的 小 没有关于SIMD,但我认为这个库允许使用比128位更广泛的硬件加速数据类型。我的HPSG解析引擎通常执行5,000+位的密集位计算向量。
  • 再次假设我没有错过这一点,为什么不把它称为System.Int128 / System.UInt128而不是Vector<T>?使用通用基元类型对其进行参数化确实带来了一定的好处,但是我错误地认为它更像是一个有用的扩展数组(即,blittable元素T),而不是只有一个双宽度的CPU寄存器,我的思想就像你能得到的“标量”一样。 不要误会我的意思,128位寄存器是有趣的,有用的,令人兴奋的东西 - 如果这里只是有点超卖?例如,无论是什么,Vector<byte>都将有16个元素,无论你是否需要或全部使用它们,所以Count的精神在运行时期望因实例而异,似乎在这里被误用。
  • 即使单个Vector<T>不能直接处理我所描述的用例,我也希望更新我当前的实现(对每个N位向量使用ulong[N >> 6]数组)而不是使用Vector<ulong>[N >> 7]数组? ...是的,那是“Vector<ulong>阵列”,这对我来说再次显得奇怪;不应该在其名称中具有“Vector”的类型是否足够或有用地可扩展而无需显式创建数组来包装多个实例?
  • 除了每个128位SIMD按位操作处理两倍数据的事实之外,每个操作码的周期内SIMD逐位运算还会更快(或更慢)吗?
  • 是否有其他常用或可用的硬件平台,System.Numerics.Vectors实际上报告了不同的SIMD位宽?
.net bit-manipulation simd hardware-acceleration
1个回答
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(Vector<long>long[2]),4×32(Vector<int>int[4]),8×16上运行(Vector<short>short[8])或16×8(Vector<byte>byte[16])位向量(或在支持它的平台上加倍宽度)。在Int128上提供“逐字节添加”操作会很奇怪,并且不提供真正的128位加法使它更加奇怪。除了前面提到的,大小不是128位的定义,这很常见。

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

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

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

© www.soinside.com 2019 - 2024. All rights reserved.