包 jdk.incubator.vector
孵化功能。 将在未来的版本中删除。
这个包提供了表示向量计算的类,如果有合适的硬件和运行时能力,可以使用向量硬件指令加速。
vector 是一个固定数量的 lanes 的序列,所有一些固定的 element type 例如 byte , long ,或 float 。每个泳道包含一个独立的元素类型值。对向量的操作通常是 lane-wise ,将一些标量运算符(例如 添加 )分布在参与向量的通道上,通常生成一个向量结果,其通道包含各种标量结果。当在支持平台上运行时,车道级操作可以由硬件并行执行。这种并行方式称为Single Instruction Multiple Data (SIMD) 并行。
在 SIMD 编程风格中,向量通道内的大多数操作都是无条件的,但条件执行的效果可以使用 masked operations 实现,例如 blend() ,在关联的 VectorMask 的控制下。使用cross-lane 操作实现除严格的车道流以外的数据移动,通常在关联的VectorShuffle 的控制下。车道数据和/或整个矢量可以使用各种车道方式重新格式化 转换 和字节方式重新格式化 重新诠释 ,通常在反射 VectorSpecies 对象的控制下,该对象选择不同于输入矢量的替代矢量格式。
Vector<E> 声明了一组对所有元素类型通用的向量操作(方法)。这些常见操作包括对通道值的一般访问、数据选择和移动、重新格式化以及所有原始类型共有的某些算术和逻辑操作(例如加法或比较)。
Public subtypes of Vector 对应特定的元素类型。这些声明了特定于该元素类型的进一步操作,包括对通道值的未装箱访问、对整数元素类型值的按位操作或对浮点元素类型值的超越操作。
一些 lane-wise 操作,例如 add 运算符,被定义为全服务命名操作,其中 Vector 上的相应方法出现在屏蔽和未屏蔽的重载中,并且(在子类中)也出现在协变覆盖(返回子类)中以及额外的标量广播重载(屏蔽和非屏蔽)。其他基于车道的操作,例如 min 运算符,被定义为部分服务(而非全服务)命名操作,其中 Vector 和/或子类上的相应方法提供一些但所有可能的重载和覆盖(通常是具有标量广播重载的未屏蔽变体)。最后,所有 lane-wise 操作(那些如前所述命名的,或者未命名的方法)都有一个相应的 operator token 声明为 VectorOperators 上的静态常量。每个运算符标记为操作定义一个符号 Java 表达式,例如 a + b 代表 ADD 运算符标记。 Vector 上提供了通用的 lane-wise 操作令牌接受方法,例如 一元车道 操作,并且与全服务命名操作具有相同的变体。
此包包含 Vector 的公共子类型,对应于每个受支持的元素类型:ByteVector 、ShortVector 、IntVector 、LongVector 、FloatVector 和 DoubleVector 。
下面是使用向量计算将两个浮点数组 a 和 b 的元素相乘并将结果存储在数组 c 中的示例。
static final VectorSpecies<Float> SPECIES = FloatVector.SPECIES_PREFERRED;
void vectorMultiply(float[] a, float[] b, float[] c) {
// It is assumed array arguments are of the same size
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() 迭代将有一个包含所有通道集的掩码。只有最后一次迭代(如果 a.length 不是 SPECIES.length() 的倍数)将具有第一个 a.length % SPECIES.length() 通道集的掩码。由于在所有迭代中都使用了掩码,因此上述实现可能无法实现最佳性能(对于大数组长度)。相同计算可以在没有掩码的情况下实现如下:
static final VectorSpecies<Float> SPECIES = FloatVector.SPECIES_PREFERRED;
void vectorMultiply(float[] a, float[] b, float[] c) {
int i = 0;
// It is assumed array arguments are of the same size
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];
}
}
向量计算之后的标量计算需要处理tail of TLENGTH数组元素,其中TLENGTH < SPECIES.length()为向量种类。上面的示例使用首选种类(FloatVector.SPECIES_PREFERRED),确保代码动态适应使其运行的平台达到最佳形状。
上面代码中使用了辅助方法 loopBound() 来查找向量循环的结尾。此处也可能使用原始掩码表达式,例如 (a.length & ~(SPECIES.length() - 1)),因为已知 SPECIES.length() 为 8,它是 2 的幂。但这并不总是正确的假设。例如,如果 FloatVector.SPECIES_PREFERRED 结果具有依赖于平台的形状 S_Max_BIT ,并且该形状具有一些奇怪的假设大小,例如 384(根据某些体系结构,这是一个有效的矢量大小),那么手动调整的原始掩码表达式可能产生令人惊讶的结果。
性能说明
该包依赖于运行时将向量操作动态编译为最佳向量硬件指令的能力。每个操作都有一个默认的标量实现,如果操作不能编译为向量指令,则使用它。生成最佳向量机代码时,用户需要注意以下几点:
- 所用矢量的形状应由底层平台支持。例如,使用
VectorShapeS_512_BIT的IntVector编写的代码将不会在仅支持 256 位向量的平台上编译为向量指令。相反,将使用默认标量实现。出于这个原因,建议使用如上所示的首选种类来编写一般大小的向量计算。 - 此包中定义的大多数类应被视为 value-based 类。此分类适用于
Vector及其子类型VectorMask、VectorShuffle和VectorSpecies。有了这些类型,==等身份敏感操作可能会产生不可预知的结果,或降低性能。奇怪的是,v.equals(w)可能比v==w更快,因为equals是 not 身份敏感方法。此外,这些对象可以存储在局部变量和参数中以及作为static final常量,但将它们存储在其他 Java 字段或数组元素中,虽然在语义上是有效的,但可能会带来性能风险。
对于这个包中的每个类,除非另有说明,否则任何引用类型的方法参数都不能为 null,并且任何 null 参数都将引发 NullPointerException 。这个事实没有单独记录这个 API 的方法。
-
类描述一个专门的
Vector表示byte值的有序不可变序列。一个专门的Vector表示double值的有序不可变序列。一个专门的Vector表示float值的有序不可变序列。一个专门的Vector表示int值的有序不可变序列。一个专门的Vector表示long值的有序不可变序列。一个专门的Vector表示short值的有序不可变序列。Vector <E>VectorMask <E>VectorMask表示boolean值的有序不可变序列。此类仅包含描述车道向量操作的静态常量,以及对它们进行分类的嵌套接口。所有运算符标记的根类型,提供对常见属性的查询,例如元数、参数和返回类型、符号名称和运算符名称。VectorShape选择了Vector的特定实现。VectorShuffle <E>VectorSpecies <E>