Package jdk.incubator.vector


package jdk.incubator.vector

孵化功能。将在未来的版本中移除。

此包提供了用于表达矢量计算的类,如果具有适当的硬件和运行时能力,则可以使用矢量硬件指令加速。

矢量是一个具有固定数量的lanes的序列,所有lanes都是某个固定的元素类型,例如bytelongfloat。每个lane包含一个独立的元素类型值。矢量上的操作通常是lane-wise的,将某个标量运算符(例如加法)分布到参与矢量的lanes中,通常生成一个矢量结果,其lanes包含各种标量结果。在支持平台上运行时,lane-wise操作可以由硬件并行执行。这种并行方式称为单指令多数据(SIMD)并行。

在SIMD编程风格中,矢量lane内的大多数操作是无条件的,但可以通过使用掩码操作(例如blend())来实现条件执行,控制在相关的VectorMask下。除了严格的lane-wise流动之外的数据移动是通过使用跨lane操作实现的,通常在相关的VectorShuffle的控制下。可以使用各种类型的lane-wise转换和字节级重新格式化重新解释来重新格式化lane数据和/或整个矢量,通常在选择与输入矢量不同的替代矢量格式的反射VectorSpecies对象的控制下。

Vector<E>声明了一组对所有元素类型通用的矢量操作(方法)。这些通用操作包括对lane值的通用访问、数据选择和移动、重新格式化以及所有原始类型通用的某些算术和逻辑操作(例如加法或比较)。

Vector的公共子类型对应于特定的元素类型。这些声明了进一步针对该元素类型特定的操作,包括对整数元素类型的值进行位操作、对浮点元素类型的值进行超越操作等。

一些lane-wise操作,例如add运算符,被定义为一个完整的命名操作,其中Vector上的相应方法具有掩码和未掩码的重载,并且(在子类中)还具有协变重写(返回子类)和额外的标量广播重载(包括掩码和未掩码)。其他lane-wise操作,例如min运算符,被定义为部分服务(不是完全服务)的命名操作,其中Vector和/或子类上的相应方法提供了一些但不是所有可能的重载和重写(通常是未掩码变体与标量广播重载)。最后,所有lane-wise操作(如先前描述的命名操作或其他未命名的方法)都在VectorOperators上声明了相应的operator token作为静态常量。每个操作符令牌定义了一个符号Java表达式,例如a + b用于ADD操作符令牌。通用的lane-wise操作令牌接受方法,例如用于一元lane-wise操作的方法,在Vector上提供,并且具有与完全服务命名操作相同的变体。

此包包含了与每种支持的元素类型对应的Vector的公共子类型:ByteVectorShortVectorIntVectorLongVectorFloatVectorDoubleVector

以下是一个示例,演示如何使用矢量计算来将两个float数组ab的元素相乘,并将结果存储在数组c中。


 static final VectorSpecies<Float> SPECIES = FloatVector.SPECIES_PREFERRED;

 void vectorMultiply(float[] a, float[] b, float[] c) {
   // 假设数组参数的大小相同
   for (int i = 0; i < a.length; i += SPECIES.length()) {
         VectorMask<Float> m = SPECIES.indexInRange(i, a.length);
         FloatVector va = FloatVector.fromArray(SPECIES, a, i, m);
         FloatVector vb = FloatVector.fromArray(SPECIES, b, i, m);
         FloatVector vc = va.mul(vb)
         vc.intoArray(c, i, m);
   }
 }
 
在上面的示例中,我们使用由indexInRange()生成的掩码来防止读取/写入超出数组长度。前a.length / SPECIES.length()次迭代将具有所有lanes设置的掩码。只有最后一次迭代(如果a.length不是SPECIES.length()的倍数)将具有设置了前a.length % SPECIES.length()个lanes的掩码。由于在所有迭代中都使用了掩码,上述实现可能无法实现最佳性能(对于大数组长度)。可以在不使用掩码的情况下实现相同的计算,如下所示:

 static final VectorSpecies<Float> SPECIES = FloatVector.SPECIES_PREFERRED;

 void vectorMultiply(float[] a, float[] b, float[] c) {
   int i = 0;
   // 假设数组参数的大小相同
   for (; i < SPECIES.loopBound(a.length); i += SPECIES.length()) {
         FloatVector va = FloatVector.fromArray(SPECIES, a, i);
         FloatVector vb = FloatVector.fromArray(SPECIES, b, i);
         FloatVector vc = va.mul(vb)
         vc.intoArray(c, i);
   }

   for (; i < a.length; i++) {
     c[i] = a[i] * b[i];
   }
 }
 
在矢量计算之后需要进行标量计算,以处理TLENGTH数组元素的尾部,其中TLENGTH < SPECIES.length()适用于矢量种类。上述示例使用了首选种类(FloatVector.SPECIES_PREFERRED),确保代码动态适应在其运行的平台上的最佳形状。

上述代码中使用了辅助方法loopBound()来找到矢量循环的结束。也可以在这里使用原始掩码表达式,例如(a.length & ~(SPECIES.length() - 1)),因为已知SPECIES.length()为8,这是2的幂。但这并不总是正确的假设。例如,如果FloatVector.SPECIES_PREFERRED的平台相关形状为S_Max_BIT,并且该形状具有一些奇怪的假设大小,例如384(根据某些架构,这是一个有效的矢量大小),那么手动调整的原始掩码表达式可能会产生令人惊讶的结果。

性能注意事项

此包依赖于运行时动态将矢量操作编译为最佳矢量硬件指令的能力。如果无法将操作编译为矢量指令,则会使用每个操作的默认标量实现。

用户需要注意以下几点以生成最佳的矢量机器代码:

  • 所使用的矢量形状必须受到底层平台的支持。例如,使用IntVectorVectorShape S_512_BIT编写的代码将不会在仅支持256位矢量的平台上编译为矢量指令。相反,将使用默认的标量实现。因此,建议使用上面显示的首选种类编写通用大小的矢量计算。
  • 此包中定义的大多数类应被视为基于值的类。这个分类适用于Vector及其子类型、VectorMaskVectorShuffleVectorSpecies。对于这些类型,==等身份敏感操作可能会产生不可预测的结果,或者性能降低。奇怪的是,v.equals(w)可能比v==w更快,因为equals不是一个身份敏感的方法。此外,这些对象可以存储在局部变量和参数中以及作为static final常量,但将它们存储在其他Java字段或数组元素中,虽然在语义上是有效的,但可能会带来性能风险。

对于此包中的每个类,除非另有说明,否则引用类型的任何方法参数都不得为null,任何null参数都将引发NullPointerException。对于此API的方法,这一事实没有单独记录。

  • Class
    Description
    代表有序不可变byte值序列的专用Vector
    代表有序不可变double值序列的专用Vector
    代表有序不可变序列的专用Vector,其中包含float值。
    代表有序不可变序列的专用Vector,其中包含int值。
    代表有序不可变序列的专用Vector,其中包含long值。
    代表有序不可变序列的专用Vector,其中包含short值。
    Vector<E>
    一个固定数量的lanes序列,所有元素类型都是固定的,如bytelongfloat
    VectorMask代表有序不可变序列的boolean值。
    这个类仅包含描述按lane进行的矢量操作的静态常量,以及对它们进行分类的嵌套接口。
    用于所有重新关联的按lane二元运算符的类型,可在表达式中使用,如e = v0.reduceLanes(ADD)
    用于所有按lane二元(两个参数)运算符的类型,可在表达式中使用,如w = v0.lanewise(ADD, v1)
    用于所有二元按lane布尔比较的类型,可在表达式中使用,如m = v0.compare(LT, v1)
    用于所有按lane值转换的类型,可在表达式中使用,如w1 = v0.convert(I2D, 1)
    所有操作符标记的根类型,提供常见属性的查询,如参数数量、参数和返回类型、符号名称和操作符名称。
    用于所有按lane三元(三个参数)运算符的类型,可在表达式中使用,如w = v0.lanewise(FMA, v1, v2)
    用于所有一元按lane布尔测试的类型,可在表达式中使用,如m = v0.test(IS_FINITE)
    用于所有按lane一元(一个参数)运算符的类型,可在表达式中使用,如w = v0.lanewise(NEG)
    VectorShape选择特定的Vector实现。
    VectorShuffle代表有序不可变序列的int值,称为源索引,其中每个源索引从兼容的Vector中选择一个源lane。
    管理具有相同element typeETYPE)和shape组合的所有矢量的接口。