Package jdk.incubator.vector
孵化功能。将在未来的版本中移除。
此包提供了用于表达矢量计算的类,如果具有适当的硬件和运行时能力,则可以使用矢量硬件指令加速。
矢量是一个具有固定数量的lanes的序列,所有lanes都是某个固定的元素类型,例如byte
、long
或float
。每个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
的公共子类型:ByteVector
、ShortVector
、IntVector
、LongVector
、FloatVector
和DoubleVector
。
以下是一个示例,演示如何使用矢量计算来将两个float数组a
和b
的元素相乘,并将结果存储在数组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(根据某些架构,这是一个有效的矢量大小),那么手动调整的原始掩码表达式可能会产生令人惊讶的结果。
性能注意事项
此包依赖于运行时动态将矢量操作编译为最佳矢量硬件指令的能力。如果无法将操作编译为矢量指令,则会使用每个操作的默认标量实现。用户需要注意以下几点以生成最佳的矢量机器代码:
- 所使用的矢量形状必须受到底层平台的支持。例如,使用
IntVector
的VectorShape
S_512_BIT
编写的代码将不会在仅支持256位矢量的平台上编译为矢量指令。相反,将使用默认的标量实现。因此,建议使用上面显示的首选种类编写通用大小的矢量计算。 - 此包中定义的大多数类应被视为基于值的类。这个分类适用于
Vector
及其子类型、VectorMask
、VectorShuffle
和VectorSpecies
。对于这些类型,==
等身份敏感操作可能会产生不可预测的结果,或者性能降低。奇怪的是,v.equals(w)
可能比v==w
更快,因为equals
不是一个身份敏感的方法。此外,这些对象可以存储在局部变量和参数中以及作为static final
常量,但将它们存储在其他Java字段或数组元素中,虽然在语义上是有效的,但可能会带来性能风险。
对于此包中的每个类,除非另有说明,否则引用类型的任何方法参数都不得为null,任何null参数都将引发NullPointerException
。对于此API的方法,这一事实没有单独记录。
-
ClassDescription代表有序不可变
byte
值序列的专用Vector
。代表有序不可变double
值序列的专用Vector
。代表有序不可变序列的专用Vector
,其中包含float
值。代表有序不可变序列的专用Vector
,其中包含int
值。代表有序不可变序列的专用Vector
,其中包含long
值。代表有序不可变序列的专用Vector
,其中包含short
值。Vector<E>一个固定数量的lanes序列,所有元素类型都是固定的,如byte
、long
或float
。VectorMask<E>VectorMask
代表有序不可变序列的boolean
值。这个类仅包含描述按lane进行的矢量操作的静态常量,以及对它们进行分类的嵌套接口。所有操作符标记的根类型,提供常见属性的查询,如参数数量、参数和返回类型、符号名称和操作符名称。VectorShape
选择特定的Vector
实现。VectorShuffle
代表有序不可变序列的int
值,称为源索引,其中每个源索引从兼容的Vector
中选择一个源lane。管理具有相同element type
(ETYPE
)和shape
组合的所有矢量的接口。