Class Vector<E>

java.lang.Object
jdk.incubator.vector.Vector<E>
类型参数:
E - ETYPE的装箱版本,向量的元素类型
直接已知子类:
ByteVector, DoubleVector, FloatVector, IntVector, LongVector, ShortVector

public abstract class Vector<E> extends Object
Vector 依赖于Java平台的预览特性:
仅当启用预览特性时,程序才能使用 Vector
预览特性可能在未来的版本中被移除,或升级为Java平台的永久特性。
一系列固定数量的lanes,全部是某种固定的元素类型,如bytelongfloat。每个lane包含一个独立的元素类型值。矢量上的操作通常是逐lane的,将某个标量运算符(如加法)分布到参与矢量的lane上,通常生成一个矢量结果,其lane包含各种标量结果。在支持平台上运行时,lane-wise操作可以由硬件并行执行。这种并行方式称为单指令多数据(SIMD)并行。

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

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

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

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

此包含有一个公共的Vector子类型,对应于每种支持的元素类型:ByteVectorShortVectorIntVectorLongVectorFloatVectorDoubleVector

矢量的元素类型,称为ETYPE,是原始类型byteshortintlong floatdouble之一。

Vector<E>中的E类型是ETYPE装箱版本。例如,在类型Vector<Integer>中,E参数是Integer,而ETYPEint。在这样的矢量中,每个lane携带一个原始int值。对于其他原始类型也是如此。 (另请参阅Java语言规范的第5.1.75.1.8节。)

矢量的长度是lane计数,即它包含的lane数。当上下文清楚指出属于哪个矢量时,这个数字也称为VLENGTH。每个矢量都有自己固定的VLENGTH,但不同的矢量实例可能具有不同的长度。VLENGTH是一个重要的数字,因为它估计了单个矢量操作相对于执行VLENGTH标量运算的SIMD性能增益。

形状和种类

矢量的信息容量由其矢量形状(也称为其VSHAPE)确定。每个可能的VSHAPEVectorShape枚举的一个成员表示,并表示为所有具有该形状的矢量共享的一种实现格式。因此,矢量的位大小由其矢量形状决定。

一些Java平台仅对一个形状提供特殊支持,而其他平台支持多个形状。一个典型的平台不太可能支持此API描述的所有形状。因此,大多数矢量操作在单个输入形状上工作,并在输出上产生相同的形状。明确记录为这样的更改形状的操作称为改变形状,而大多数操作是形状不变的,以避免使只支持一个形状的平台处于不利地位。有查询可用于发现当前Java平台的一般SIMD计算的首选形状,或任何给定lane类型的最大可用形状。为了可移植性,使用此API的代码应该首先查询支持的形状,然后使用所选形状内的形状不变操作处理所有数据。

每个元素类型和矢量形状的唯一组合确定了一个唯一的矢量种类。矢量种类由一个固定的VectorSpecies<E>实例共同表示,所有具有相同形状和ETYPE的矢量共享。

除非另有说明,逐lane矢量操作要求所有矢量输入具有完全相同的VSHAPEVLENGTH,也就是说,它们必须具有完全相同的种类。这允许相应的lane被明确配对。check()方法提供了一种明确执行此检查的简单方法。

矢量形状、VLENGTHETYPE都是相互约束的,因此VLENGTH乘以每个lane的位大小必须始终匹配矢量形状的位大小。因此,重新解释矢量可能会使其长度加倍,如果且仅当它减半lane大小,或者改变形状。同样,重新解释矢量可能会使lane大小加倍,如果且仅当它减半长度,或者改变矢量的形状。

矢量子类型

Vector声明了一组对所有元素类型都通用的矢量操作(方法)(如加法)。具有具体元素类型的Vector子类声明了进一步针对该元素类型的特定操作(如访问lane中元素值、对整数元素类型值的逻辑操作,或对浮点元素类型值的超越操作)。有六个抽象的Vector子类对应于支持的一组元素类型,ByteVectorShortVectorIntVectorLongVectorFloatVectorDoubleVector。除了类型特定的操作外,这些类还支持创建矢量值(Vector的实例)。它们公开与支持的种类对应的静态常量,并且这些类型上的静态方法通常将种类作为参数。例如,FloatVector.fromArray创建并返回指定种类的浮点矢量,其中元素从指定的浮点数组加载。建议将Species实例保存在static final字段中,以便运行时编译器能够最佳地创建和使用矢量值。

作为类型化向量类定义的静态常量的示例,常量FloatVector.SPECIES_256是唯一的种类,其通道为float,矢量大小为256位。同样,常量FloatVector.SPECIES_PREFERRED是最适合在当前运行的Java平台上处理float向量通道的种类。

另一个例子是,通过调用DoubleVector.broadcast(dsp, 0.5)可以获得广播标量值(double)0.5,但需要参数dsp来选择结果向量的种类(因此选择形状和长度)。

通道操作

在定义向量操作时,我们使用术语通道。向量中的通道数是它包含的标量元素数。例如,类型为float且形状为S_256_BIT的向量有八个通道,因为32*8=256

大多数向量操作都是通道操作,这意味着操作由基础标量运算符组成,该运算符针对输入向量的每个不同通道重复。如果有其他相同类型的向量参数,它们的通道将与第一个输入向量的通道对齐。(它们必须都具有共同的VLENGTH。)对于大多数通道操作,由通道操作产生的输出将具有与操作的输入的VLENGTH相等的VLENGTH。因此,这些通道操作是长度不变的,在它们的基本定义中。

长度不变原则与另一个基本原则相结合,即大多数长度不变的通道操作也是形状不变的,这意味着通道操作的输入和输出将具有共同的VSHAPE。当原则发生冲突时,因为逻辑结果(具有不变的VLENGTH)不适合不变的VSHAPE,则通过特殊约定明确处理结果的扩展和收缩。

向量操作可以分为各种类别,并且它们的行为通常可以用基础标量运算符来指定。在下面的示例中,ETYPE是操作的元素类型(例如int.class),EVector是相应的具体向量类型(例如IntVector.class)。

  • 一个逐条的一元操作,例如w = v0.neg(),接受一个输入向量,将一元标量运算符分布到各个条,并产生一个相同类型和形状的结果向量。对于输入向量a的每个条,将应用基础标量运算符到条值。结果被放置到相同条的结果向量中。以下伪代码说明了这种操作类别的行为:
    
     ETYPE scalar_unary_op(ETYPE s);
     EVector a = ...;
     VectorSpecies<E> species = a.species();
     ETYPE[] ar = new ETYPE[a.length()];
     for (int i = 0; i < ar.length; i++) {
         ar[i] = scalar_unary_op(a.lane(i));
     }
     EVector r = EVector.fromArray(species, ar, 0);
     
  • 一个逐条的二元操作,例如w = v0.add(v1),接受两个输入向量,将二元标量运算符分布到各个条,并产生一个相同类型和形状的结果向量。对于两个输入向量ab的每个条,将应用基础标量运算符到条值。结果被放置到相同条的结果向量中。以下伪代码说明了这种操作类别的行为:
    
     ETYPE scalar_binary_op(ETYPE s, ETYPE t);
     EVector a = ...;
     VectorSpecies<E> species = a.species();
     EVector b = ...;
     b.check(species);  // 必须具有相同的species
     ETYPE[] ar = new ETYPE[a.length()];
     for (int i = 0; i < ar.length; i++) {
         ar[i] = scalar_binary_op(a.lane(i), b.lane(i));
     }
     EVector r = EVector.fromArray(species, ar, 0);
     
  • 从一元和二元操作泛化,一个逐条的n元操作接受N个输入向量v[j],将n元标量运算符分布到各个条,并产生一个相同类型和形状的结果向量。除了一些三元操作,例如w = v0.fma(v1,v2),此API不支持逐条的n元操作。对于所有输入向量v[j]的每个条,将应用基础标量运算符到条值。结果被放置到相同条的结果向量中。以下伪代码说明了这种操作类别的行为:
    
     ETYPE scalar_nary_op(ETYPE... args);
     EVector[] v = ...;
     int N = v.length;
     VectorSpecies<E> species = v[0].species();
     for (EVector arg : v) {
         arg.check(species);  // 所有必须具有相同的species
     }
     ETYPE[] ar = new ETYPE[a.length()];
     for (int i = 0; i < ar.length; i++) {
         ETYPE[] args = new ETYPE[N];
         for (int j = 0; j < N; j++) {
             args[j] = v[j].lane(i);
         }
         ar[i] = scalar_nary_op(args);
     }
     EVector r = EVector.fromArray(species, ar, 0);
     
  • 一个逐条的转换操作,例如w0 = v0.convert(VectorOperators.I2D, 0),接受一个输入向量,将一元标量转换运算符分布到各个条,并产生转换后值的逻辑结果。逻辑结果(或至少部分)以与输入向量相同形状的向量呈现。

    与其他逐条操作不同,转换可以改变条类型,从输入(域)类型到输出(范围)类型。条大小可能随类型改变而改变。为了管理大小变化,逐条转换方法可以生成部分结果,在一个part参数的控制下,这在其他地方有解释。(根据上面的示例,第二组转换后的条值可以通过w1 = v0.convert(VectorOperators.I2D, 1)获得。)

    以下伪代码说明了这种操作类别在特定示例中从intdouble的转换行为,保留较低或较高条(取决于part)以保持形状不变:

    
     IntVector a = ...;
     int VLENGTH = a.length();
     int part = ...;  // 0或1
     VectorShape VSHAPE = a.shape();
     double[] arlogical = new double[VLENGTH];
     for (int i = 0; i < limit; i++) {
         int e = a.lane(i);
         arlogical[i] = (double) e;
     }
     VectorSpecies<Double> rs = VSHAPE.withLanes(double.class);
     int M = Double.BITS / Integer.BITS;  // 扩展因子
     int offset = part * (VLENGTH / M);
     DoubleVector r = DoubleVector.fromArray(rs, arlogical, offset);
     assert r.length() == VLENGTH / M;
     
  • 一个跨条约简操作,例如e = v0.reduceLanes(VectorOperators.ADD),作用于输入向量的所有条元素。一个累积函数应用于所有条元素以产生一个标量结果。如果约简操作是可结合的,则结果可以通过使用指定的可结合标量二元操作和单位值以任意顺序操作条元素进行累积。否则,约简操作指定了累积的顺序。以下伪代码说明了如果是可结合的这种操作类别的行为:
    
     ETYPE assoc_scalar_binary_op(ETYPE s, ETYPE t);
     EVector a = ...;
     ETYPE r = <单位值>;
     for (int i = 0; i < a.length(); i++) {
         r = assoc_scalar_binary_op(r, a.lane(i));
     }
     
  • 一个跨条移动操作,例如w = v0.rearrange(shuffle)作用于输入向量的所有条元素,并以数据相关方式将它们移动到输出向量中的不同条。移动由辅助数据引导,例如VectorShuffle或定义移动起始点的标量索引。以下伪代码说明了这种操作类别的行为,对于洗牌的情况:
    
     EVector a = ...;
     Shuffle<E> s = ...;
     ETYPE[] ar = new ETYPE[a.length()];
     for (int i = 0; i < ar.length; i++) {
         int source = s.laneSource(i);
         ar[i] = a.lane(source);
     }
     EVector r = EVector.fromArray(a.species(), ar, 0);
     
  • 一个掩码操作是前述操作(逐条或跨条之一)的变体,其中操作接受额外的尾随VectorMask参数。在设置了掩码的条中,操作的行为就好像没有掩码参数一样,但在未设置掩码的条中,基础标量操作被抑制。掩码操作在其他地方有更详细的解释
  • 一个非常特殊的掩码逐条二元操作是一个混合,它在两个输入向量ab上逐条操作,根据掩码m从一个输入或另一个中选择条值。在设置了m的条中,从b中选择相应值到结果中;否则从a中选择值。因此,混合充当了Java的三元选择表达式m?b:a的矢量化版本:
    
     ETYPE[] ar = new ETYPE[a.length()];
     for (int i = 0; i < ar.length; i++) {
         boolean isSet = m.laneIsSet(i);
         ar[i] = isSet ? b.lane(i) : a.lane(i);
     }
     EVector r = EVector.fromArray(species, ar, 0);
     
  • 一个逐条的二元测试操作,例如m = v0.lt(v1),接受两个输入向量,将二元标量比较分布到各个条,并产生,不是布尔向量,而是一个向量掩码。对于两个输入向量ab的每个条,将应用基础标量比较运算符到条值。得到的布尔值被放置到相同条的向量掩码结果中。以下伪代码说明了这种操作类别的行为:
    
     boolean scalar_binary_test_op(ETYPE s, ETYPE t);
     EVector a = ...;
     VectorSpecies<E> species = a.species();
     EVector b = ...;
     b.check(species);  // 必须具有相同的species
     boolean[] mr = new boolean[a.length()];
     for (int i = 0; i < mr.length; i++) {
         mr[i] = scalar_binary_test_op(a.lane(i), b.lane(i));
     }
     VectorMask<E> m = VectorMask.fromArray(species, mr, 0);
     
  • 类似于二元比较,一个逐条的一元测试操作,例如m = v0.test(IS_FINITE),接受一个输入向量,将一个标量谓词(一个测试函数)分布到各个条,并产生一个向量掩码

如果一个向量操作不属于上述任何一类,则方法文档明确指定了它如何处理输入向量的条,并在适当的情况下使用伪代码说明了行为。

大多数逐条的二元和比较操作提供了方便的重载,接受标量作为第二个输入,而不是向量。在这种情况下,标量值通过广播到与第一个输入相同的条结构中。例如,要将double向量的所有条乘以标量值1.1,表达式v.mul(1.1)比使用显式广播操作的等效表达式更容易处理,例如v.mul(v.broadcast(1.1))v.mul(DoubleVector.broadcast(v.species(), 1.1))。除非另有说明,标量变体始终表现为将每个标量值首先转换为与第一个向量输入相同species的向量,使用适当的broadcast操作。

掩码操作

许多向量操作接受一个可选的mask参数,选择哪些条参与基础标量运算符。如果存在,掩码参数出现在方法参数列表的末尾。

掩码参数的每个条是一个布尔值,可以是设置未设置状态。对于未设置掩码参数的条,基础标量操作被抑制。通过这种方式,掩码允许向量操作模拟标量控制流操作,而不会失去SIMD并行性,除非掩码条未设置。

被掩码抑制的操作永远不会引起任何异常或任何形式的副作用,即使基础标量操作可能会导致异常。例如,看似访问超出边界数组元素或将整数值除以零的未设置条将被忽略。被抑制的条中的值永远不会参与或出现在整体操作的结果中。

对于被抑制操作对应的结果通道将填充一个默认值,该默认值取决于具体的操作,如下所示:

  • 如果被屏蔽的操作是一元、二元或 n 元算术或逻辑操作,则被抑制的通道将从第一个矢量操作数(即接收方法调用的矢量)中填充,就像通过 blend 一样。
  • 如果被屏蔽的操作是内存加载或从另一个矢量进行 slice(),则被抑制的通道不会被加载,并且将使用 ETYPE 的默认值填充,该默认值在每种情况下都由所有零位组成。未设置的通道永远不会引发异常,即使假设的相应内存位置不存在(因为超出数组的索引范围)。
  • 如果操作是带有提供通道索引的操作数的跨通道操作(类型为 VectorShuffleVector),则不会计算被抑制的通道,并且将使用零默认值填充。通常,无效的通道索引会引发 IndexOutOfBoundsException,但如果通道未设置,则会静默地替换为零值,无论索引如何。此规则类似于前面的规则,用于被屏蔽的内存加载。
  • 如果被屏蔽的操作是内存存储或向另一个矢量进行 unslice(),则被抑制的通道不会被存储,并且相应的内存或矢量位置(如果有)将保持不变。

    (注意:对于被抑制的通道,永远不会发生诸如竞争条件之类的内存效果。也就是说,实现不会秘密地重新写入未设置通道的现有值。在 Java 内存模型中,将内存变量重新分配为其当前值并不是一个无操作;它可能会悄悄地撤消另一个线程的竞争存储。)

  • 如果被屏蔽的操作是一个约简操作,则在约简中忽略被抑制的通道。如果所有通道都被抑制,则根据具体的约简操作返回一个适当的中性值,并由该方法的被屏蔽变体记录。 (这意味着用户可以通过在具有全未设置掩码的虚拟矢量上执行约简来以程序方式获取中性值。)
  • 如果被屏蔽的操作是一个比较操作,则结果掩码中的被抑制输出通道本身未设置,就好像被抑制的比较操作返回 false,而不考虑被抑制的输入值。实际上,就好像执行了未屏蔽的比较操作,然后将结果与控制掩码相交。
  • 在其他情况下,例如被屏蔽的 跨通道移动,掩码效果的具体影响由该方法的被屏蔽变体记录。

例如,对两个输入矢量 ab 进行屏蔽的二元操作会抑制掩码未设置的通道的二元操作,并保留来自 a 的原始通道值。以下伪代码说明了这种行为:


 ETYPE scalar_binary_op(ETYPE s, ETYPE t);
 EVector a = ...;
 VectorSpecies<E> species = a.species();
 EVector b = ...;
 b.check(species);  // 必须具有相同的规格
 VectorMask<E> m = ...;
 m.check(species);  // 必须具有相同的规格
 boolean[] ar = new boolean[a.length()];
 for (int i = 0; i < ar.length; i++) {
     if (m.laneIsSet(i)) {
         ar[i] = scalar_binary_op(a.lane(i), b.lane(i));
     } else {
         ar[i] = a.lane(i);  // 来自第一个输入
     }
 }
 EVector r = EVector.fromArray(species, ar, 0);
 

通道顺序和字节顺序

存储在给定矢量中的通道值的数量被称为其 矢量长度VLENGTH。有必要将矢量通道视为从第一个到最后一个按顺序排列,其中第一个通道编号为 0,下一个通道编号为 1,依此类推,直到最后一个通道编号为 VLENGTH-1。这是一种时间顺序,其中较低编号的通道被认为比较早,而较高编号(后面)的通道被认为比较晚。此 API 更倾向于使用这些术语,而不是空间术语,如“左”、“右”、“高”和“低”。

时间术语对于矢量非常有效,因为它们(通常)表示长序列中的小固定大小段,其中工作负载在时间上按顺序从开始到结束遍历。 (这是一种心理模型:它并不排除多核分治技术。)因此,当标量循环转换为矢量循环时,工作负载中相邻的标量项(一个早些时候,一个稍后)最终成为单个矢量中相邻的通道(再次,一个早些时候,一个稍后)。在矢量边界处,较早矢量中的最后一个通道项目与紧随其后的下一个矢量中的第一个通道项目相邻且紧挨在一起。

有时也会将矢量视为空间术语,其中第一个通道放置在某种虚拟纸张的边缘,随后的通道按顺序呈现在其旁边。在使用空间术语时,所有方向都同样合理:一些矢量符号将通道从左到右呈现,而其他符号将通道从右到左呈现;还有一些将通道从上到下或反之亦然。使用时间语言(之前、之后、第一个、最后一个)而不是空间语言(左、右、高、低)通常更有可能避免误解。

将时间术语与矢量通道的字节顺序联系起来的第二个原因是,术语“左”、“右”、“高”和“低”通常用于描述标量值中位之间的关系。给定类型中最左侧或最高位很可能是符号位,而最右侧或最低位很可能是算术上最不显著的位,等等。然而,将这些术语应用于矢量通道可能会引起混淆,因为相邻的矢量通道很少会发现算术上一个通道比其邻居更重要,即使在这些情况下,也没有一般方法知道哪个邻居更重要。

综合这些术语,我们将矢量的信息结构视为一系列通道的时间序列(“第一个”、“下一个”、“更早”、“更晚”、“最后”等)的位串,这些位串在内部按空间顺序排序(从“低”到“高”或从“右”到“左”)。通道中的原始值是从这些位串中解码的,通常的方式。大多数矢量操作,就像大多数 Java 标量运算符一样,将原始值视为原子值,但有些操作会显示内部位串结构。

当从内存加载矢量或将其存储回内存时,矢量通道的顺序始终与内存容器的固有顺序一致。这是真实的,无论单个通道元素是否受到由字节顺序的细节引起的“字节交换”的影响。因此,虽然矢量的标量通道元素可能会“字节交换”,但通道本身永远不会重新排序,除非通过执行跨通道重新排序的显式方法调用。

当矢量通道值存储到相同类型的 Java 变量时,只有在矢量硬件实现需要进行字节交换时才会执行字节交换。因此,字节交换是无条件的且不可见的。

作为一个有用的虚构,此 API 呈现了一个一致的幻觉,即矢量通道字节以 小端序 组成更大的通道标量。这意味着将矢量存储到 Java 字节数组中将在所有平台上以小端序显示矢量通道值的连续字节,而不考虑本机内存顺序,也不考虑矢量单元寄存器内的字节顺序(如果有的话)。

当应用 重新解释转换 时,以一种丢弃并重新绘制通道边界的方式,同时保持矢量位不变。在这种操作中,两个相邻通道将为单个新通道(或反之亦然)提供字节,并且两个通道的顺序将确定单个通道中字节的算术顺序。在这种情况下,小端约定提供了可移植的结果,因此在所有平台上,较早的通道倾向于提供较低(向右)位,而较晚的通道倾向于提供较高(向左)位。在 重新解释转换 和其他非字节矢量之间的 ByteVector 使用此约定来澄清其可移植语义。

将通道顺序与每通道字节顺序相关联的小端虚构略优于等效的大端虚构,因为某些相关公式要简单得多,特别是在通道结构更改后重新编号字节的情况下。最早的字节在所有通道结构更改中始终保持最早,但仅当使用小端约定时。根本原因是标量中的字节从最不显著(最右)到最显著(最左)编号,几乎从不相反。如果我们习惯将符号位编号为零(如某些计算机上),那么此 API 将寻求大端虚构以创建统一的矢量字节寻址。

内存操作

如前所述,矢量可以从内存加载并存储回去。可选的掩码可以控制读取或写入哪些单个内存位置。矢量的形状决定它将占用多少内存。在没有掩码的情况下,实现通常具有这样的特性,即通道存储为内存中紧密的连续值序列,与标量类型数组中的单个标量值的紧密(无间隙)系列相同。在这种情况下,内存顺序与通道顺序完全对应。第一个矢量通道值占据内存中的第一个位置,依此类推,直到矢量的长度。此外,存储的矢量通道的内存顺序与 Java 数组或 MemorySegmentPREVIEW 中的递增索引值对应。

用于通道存储的字节顺序是这样选择的,以便可以将存储的矢量值作为单个原始值读取或写入,位于保存矢量的数组或段内,产生与矢量内通道值相同的值。这一事实独立于矢量内部的通道值以小端序存储的便利虚构。

例如,FloatVector.fromArray(fsp,fa,i) 创建并返回一个特定种类fsp的浮点向量,其中的元素从某个浮点数组fa加载。第一个lane从fa[i]加载,最后一个lane从fa[i+VL-1]加载,其中VL是从种类fsp派生出的向量的长度。然后,fv=fv.add(fv2)将产生另一个相同种类fsp的浮点向量,给定一个相同种类fsp的向量fv2。接下来,mnz=fv.compare(NE, 0.0f)测试结果是否为零,产生一个掩码mnz。非零lane(仅这些lane)可以使用语句fv.intoArray(fa,i,mnz)存回原始数组元素。

扩展、收缩和部分结果

由于向量的大小是固定的,经常会出现逻辑操作的结果与所提议的输出向量的物理大小不同的情况。为了鼓励尽可能可移植和可预测的用户代码,此API对这种调整大小的向量操作设计了系统化的方法。

作为基本原则,逐lane操作是长度不变的,除非明确标记为其他。长度不变意味着如果VLENGTH个lane参与操作,那么输出也将有相同数量的lane,没有丢弃任何lane,也没有额外的填充。

作为第二个原则,有时与第一个原则相矛盾,逐lane操作也是形状不变的,除非明确标记为其他。形状不变意味着对于典型计算,VSHAPE是恒定的。在整个计算过程中保持相同的形状有助于确保稀缺的向量资源得到有效利用。(在某些硬件平台上,形状的改变可能导致不希望的效果,如额外的数据移动指令、通过内存的往返或流水线气泡。)

这些原则之间的紧张关系出现在一个操作产生的逻辑结果太大而无法适应所需输出VSHAPE的情况下。在其他情况下,当逻辑结果小于输出VSHAPE的容量时,逻辑结果的定位就成了一个问题,因为物理输出向量必须包含逻辑结果和填充的混合。

在第一种情况下,即将过大的逻辑结果塞入过小的输出VSHAPE中,我们称数据已经扩展。换句话说,一个扩展操作导致输出形状溢出。对称地,在逻辑结果较小而适合宽敞的输出VSHAPE的情况下,数据已经收缩,并且收缩操作需要输出形状用额外的零lane填充自身。

在这两种情况下,我们可以谈论一个参数M,它衡量了逻辑结果大小(以位为单位)与实际输出形状的位大小之间的扩展比率收缩比率。当向量形状发生变化,而lane大小不变时,M就是输出形状与逻辑结果的整数比率。(除了最大形状可能的情况外,所有向量大小都是2的幂,因此比率M始终是整数。在假设的非整数比率的情况下,值M将四舍五入到下一个整数,然后相同的一般考虑将适用。)

如果逻辑结果大于物理输出形状,这种形状变化必然会丢弃结果lane(除了逻辑结果的1/M)。如果逻辑大小小于输出,形状变化必须引入填充的零lane(除了物理输出的1/M)。第一种情况,丢弃lane,是一个扩展,而第二种情况,添加填充lane,是一个收缩。

同样,考虑一个逐lane转换操作,它保持形状不变,但通过比率M改变了lane大小。如果逻辑结果大于输出(或输入),这种转换必须通过M减少输出的VLENGTH个lane,丢弃除逻辑结果lane的1/M。与之前一样,丢弃lane是扩展的标志。逐lane操作通过比率M收缩lane大小,必须将VLENGTH增加相同的因子M,用零填充额外的lane;因为必须添加填充,这是一个收缩。

还有可能(尽管有些混乱)在一次操作中同时改变lane大小和容器大小,进行同时执行lane转换重塑。如果这样做,同样的规则适用,但逻辑结果大小是输入大小乘以任何来自lane大小变化的扩展或收缩比率。

为了完整起见,我们还可以谈论此API的原位操作,用于频繁情况下不发生调整大小的情况。对于原位操作,数据只是从逻辑输出简单地复制到其物理容器中,没有截断或填充。在这种情况下,比率参数M是单位。

请注意,收缩与扩展的分类取决于逻辑结果和物理输出容器的相对大小。输入容器的大小可能大于或小于其他两个值中的任何一个,而不会改变分类。例如,从128位形状转换为256位形状在许多情况下将是一个收缩,但如果与从bytelong的转换结合,那么它将是一个扩展,因为在这种情况下,逻辑结果将是1024位大小。这个例子也说明了逻辑结果不需要对应于任何特定的平台支持的向量形状。

虽然逐lane掩码操作可以被视为产生部分操作,但它们在(此API中)不被分类为扩展或收缩。从数组中掩码加载肯定会产生一个部分向量,但没有任何有意义的“逻辑输出向量”可以从这个部分结果中收缩。

这些术语需要一些注意,因为是数据,而不是容器大小,在相对于其输出容器的大小进行扩展或收缩。因此,将128位输入调整为512位向量的效果是收缩。尽管128位的有效载荷大小没有改变,但我们可以说它在新的512位容器中“看起来更小”,这将捕捉到情况的实际细节。

如果一个向量方法可能扩展其数据,它会接受一个额外的int参数,称为part,或者“部分编号”。部分编号必须在[0..M-1]范围内,其中M是扩展比率。部分编号选择逻辑结果中的M个连续不相交的等大小lane块,并将这些lane块填充到物理输出向量中。

具体来说,从扩展的逻辑结果中选择的lane编号在范围[R..R+L-1]内,其中L是物理输出向量的VLENGTH,块的起始位置Rpart*L

对于任何可能收缩其数据的向量方法,也适用类似的约定。这样的方法还接受一个额外的部分编号参数(再次称为part),它将收缩的数据lane引导到物理输出向量中的M个连续不相交的等大小lane块中。其余的lane将填充为零,或者根据方法指定的方式。

具体来说,数据被引导到编号范围[R..R+L-1]的lane中,其中L是逻辑结果向量的VLENGTH,块的起始位置R再次是由部分编号选择的|part|*L的倍数。

在收缩的情况下,部分编号必须在非正范围[-M+1..0]内。采用这种约定是因为一些方法可以以数据相关的方式执行扩展和收缩,额外的部分编号符号作为错误检查。如果向量方法接受一个部分编号并被调用执行原位操作(既不收缩也不扩展),则part参数必须恰好为零。超出允许范围的部分编号将引发索引异常。请注意,在所有情况下,零部分编号是有效的,并且对应于尽可能保留逻辑结果开头的lane并将其放入物理输出容器开头的操作。这通常是一个理想的默认值,因此零部分编号在所有情况下都是安全的,并且在大多数情况下都是有用的。

此API的各种调整大小操作将其数据收缩或扩展如下:

  • Vector.convert()将根据比率M扩展(或收缩)其操作数,如果其输出的元素大小M的因子更大(或更小)。如果输入和输出的元素大小相同,则convert()是一个原地操作。
  • Vector.convertShape()将根据比率M扩展(或收缩)其操作数,如果其逻辑结果的位大小比其输出形状的位大小大(或小)。逻辑结果的大小定义为输出的元素大小乘以其输入的VLENGTH。根据更改的lane大小比例,逻辑大小可能(在各种情况下)比输入向量大或小,独立于操作是扩展还是收缩。
  • 由于Vector.castShape()convertShape()的一个便利方法,因此它的分类作为扩展或收缩与convertShape()相同。
  • Vector.reinterpretShape()是一个扩展(或收缩)比率为M的操作,如果其输入的向量位大小被压缩到一个更小(或放入一个更大)的输出容器中,比率为M。否则,这是一个原地操作。由于这个方法是一个重新解释转换,可以擦除和重新绘制lane边界以及修改形状,输入向量的lane大小和lane计数对于其作为扩展或收缩的分类是无关紧要的。
  • unslice()方法扩展比率为M=2,因为单个输入切片被定位并插入到两个连续背景向量中的某个位置。部分编号选择第一个或第二个背景向量,由插入的切片更新。请注意,相应的slice()方法,虽然与unslice()方法相反,但不会收缩其数据,因此不需要部分编号。这是因为slice()提供了从两个输入向量中提取的确切VLENGTH lanes的切片。
在执行任何扩展或收缩操作之前,可以使用partLimit()方法在VectorSpecies上查询提议扩展或收缩操作的部分参数的限制值。从partLimit()返回的值对于扩展为正值,对于收缩为负值,对于原地操作为零。其绝对值是参数M,因此它作为相关方法的有效部分编号参数的独占限制。因此,对于扩展,partLimit()M是部分编号的独占上限,而对于收缩,partLimit()-M是独占的下限

跨lane移动数据

不会重新绘制lane或更改species的跨lane方法更加常规化且更易于推理。这些操作包括:
  • slice()系列方法,从连接的两个向量中的给定起始点提取连续的VLENGTH字段。
  • unslice()系列方法,在给定起始点将连续的VLENGTH字段插入到连接的两个向量中。
  • rearrange()系列方法,从一个或两个输入向量中选择任意一组VLENGTH lanes,并以任意顺序组装它们。lane的选择和顺序由VectorShuffle对象控制,该对象充当将源lane映射到目标lane的路由表。一个VectorShuffle可以编码数学排列以及许多其他数据移动模式。
  • compress(VectorMask)expand(VectorMask)方法,从输入向量中选择最多VLENGTH lanes,并按lane顺序组装它们。lane的选择由VectorMask控制,通过压缩或扩展按lane顺序设置lane元素,将源lane映射到目标lane。

一些向量操作不是基于lane的,而是在lane边界之间移动数据。这样的操作在SIMD代码中通常很少见,尽管有时对于需要在低级别操作数据格式的特定算法是必要的,和/或需要SIMD数据以复杂的本地模式移动。在此API中,这样的方法始终是清晰可识别的,因此可以自信地将更简单的基于lane的推理应用于代码的其余部分。

在某些情况下,向量lane边界被丢弃并“从头开始重新绘制”,以便给定输入lane中的数据可能(在几个部分中)分布到几个输出lane中,或者(相反)来自几个输入lane的数据可能被合并到单个输出lane中。可以重新绘制lane边界的基本方法是reinterpretShape()。基于这种方法,某些便利方法,如reinterpretAsBytes()reinterpretAsInts(),将(潜在地)重新绘制lane边界,同时保持相同的整体向量形状。

产生或消耗标量结果的操作可以被视为非常简单的跨lane操作。reduceLanes()系列方法将所有lane(或选择的lane)折叠在一起,并返回单个结果。作为其反向操作,broadcast系列方法可以被视为在另一个方向上跨越lane,从标量到输出向量的所有lane。单lane访问方法,如lane(I)withLane(I,E)也可以被视为非常简单的跨lane操作。

同样,将非字节向量移动到字节数组或从字节数组移动的方法可以被视为跨lane操作,因为向量lane必须分布到单独的字节中,或者(在另一个方向上)从数组字节中合并。

实现注意事项:

硬件平台依赖性和限制

Vector API 旨在加速计算,采用单指令多数据(SIMD)的风格,利用可用的硬件资源,如向量硬件寄存器和向量硬件指令。该API旨在有效利用多个SIMD硬件平台。

即使在不包括用于SIMD计算的专用硬件支持的Java平台上,此API也能正常工作。在这些平台上,Vector API 不太可能提供任何特殊的性能优势。

当前的实现经过优化,最适用于:

  • 支持至少AVX2到AVX-512的Intel x64平台。目前不支持使用掩码寄存器和AVX-512上的掩码接受硬件指令。
  • 支持NEON的ARM AArch64平台。尽管API已经设计为确保ARM SVE指令可以被支持(向量大小在128到2048位之间),但目前没有实现这些指令和一般的掩码功能。
当前的实现通过将未掩码的逐个通道操作与 blend 组合来以跨平台方式支持掩码通道操作,如表达式 a.blend(a.lanewise(op, b), m),其中 ab 是向量,op 是向量操作,m 是掩码。

当前的实现不支持浮点传统函数的最佳矢量化指令(如操作符 SINLOG)。

不装箱原始类型

尽管像 Vector<Integer> 这样的向量类型可能看起来可以与装箱的 Integer 值一起工作,但通过使每个向量子类型在内部处理实际 ETYPE(如 int)的通道值,避免了与装箱相关的开销。

基于值的类和标识操作

Vector,以及其所有子类型和许多辅助类型(如 VectorMaskVectorShuffle),都是 基于值的 类。

一旦创建,向量就不会被改变,即使只有 更改单个通道。始终会创建一个新的向量来保存新的通道值配置。禁止变异方法是抑制所有向量的对象标识作为基于值的类的必要结果。

使用 Vector 时,== 这样的标识敏感操作可能导致不可预测的结果,或降低性能。奇怪的是,v.equals(w) 可能比 v==w 更快,因为 equals 不是 一个标识敏感方法。此外,这些对象可以存储在局部变量和参数中,作为 static final 常量,但将它们存储在其他Java字段或数组元素中,虽然在语义上是有效的,但可能会导致性能损失。

  • Method Details

    • species

      public abstract VectorSpecies<E> species()
      返回此向量的种类。
      返回:
      此向量的种类
    • elementType

      public abstract Class<E> elementType()
      返回此向量的原始元素类型ETYPE)。
      实现要求:
      这与this.species().elementType()的值相同。
      返回:
      此向量的原始元素类型
    • elementSize

      public abstract int elementSize()
      返回此向量的每个通道的位大小,以位为单位。
      实现要求:
      这与this.species().elementSize()的值相同。
      返回:
      此向量的通道大小,以位为单位
    • shape

      public abstract VectorShape shape()
      返回此向量的形状。
      实现要求:
      这与this.species().vectorShape()的值相同。
      返回:
      此向量的形状
    • length

      public abstract int length()
      返回通道计数,或向量长度VLENGTH)。
      返回:
      通道计数
    • bitSize

      public abstract int bitSize()
      返回此向量的总大小,以位为单位。
      实现要求:
      这与this.shape().vectorBitSize()的值相同。
      返回:
      此向量的总大小,以位为单位
    • byteSize

      public abstract int byteSize()
      返回此向量的总大小,以字节为单位。
      实现要求:
      这与this.bitSize()/Byte.SIZE的值相同。
      返回:
      此向量的总大小,以字节为单位
    • lanewise

      public abstract Vector<E> lanewise(VectorOperators.Unary op)
      对此向量的通道值进行操作。这是一个逐通道一元操作,将所选操作应用于每个通道。
      API 注意:
      子类型通过提高方法返回类型来改进此方法。
      参数:
      op - 用于处理通道值的操作
      返回:
      将操作逐通道应用于输入向量的结果
      抛出:
      UnsupportedOperationException - 如果此向量不支持请求的操作
      参见:
    • lanewise

      public abstract Vector<E> lanewise(VectorOperators.Unary op, VectorMask<E> m)
      对此向量的通道值进行操作,通过掩码控制选择通道元素。这是一个逐通道一元操作,将所选操作应用于每个通道。
      API 注意:
      子类型通过提高方法返回类型来改进此方法。
      参数:
      op - 用于处理通道值的操作
      m - 控制通道选择的掩码
      返回:
      将操作逐通道应用于输入向量的结果
      抛出:
      UnsupportedOperationException - 如果此向量不支持请求的操作
      参见:
    • lanewise

      public abstract Vector<E> lanewise(VectorOperators.Binary op, Vector<E> v)
      将此向量的相应通道值与第二个输入向量的相应通道值组合。这是一个逐通道二元操作,将所选操作应用于每个通道。
      API 注意:
      子类型通过提高方法返回类型来改进此方法。
      参数:
      op - 用于组合通道值的操作
      v - 输入向量
      返回:
      将操作逐通道应用于两个输入向量的结果
      抛出:
      UnsupportedOperationException - 如果此向量不支持请求的操作
      参见:
    • lanewise

      public abstract Vector<E> lanewise(VectorOperators.Binary op, Vector<E> v, VectorMask<E> m)
      将此向量的相应通道值与第二个输入向量的相应通道值组合,通过掩码控制选择通道元素。这是一个逐通道二元操作,将所选操作应用于每个通道。
      API注释:
      子类型通过提高方法返回类型来改进此方法。
      参数:
      op - 用于组合通道值的操作
      v - 第二个输入向量
      m - 控制通道选择的掩码
      返回:
      将操作逐通道应用于两个输入向量的结果
      抛出:
      UnsupportedOperationException - 如果此向量不支持请求的操作
      参见:
    • lanewise

      public abstract Vector<E> lanewise(VectorOperators.Binary op, long e)
      将此向量的通道值与广播标量值组合。这是一种逐通道二元操作,将所选操作应用于每个通道。返回值将等于此表达式:this.lanewise(op, this.broadcast(e))
      API注释:
      longe必须能够准确表示为此向量种类的ETYPE,以便e==(long)(ETYPE)e。此规则由对broadcast()的隐式调用强制执行。

      子类型通过提高方法返回类型和标量参数e的类型来改进此方法。

      参数:
      op - 用于组合通道值的操作
      e - 输入标量
      返回:
      将操作逐通道应用于输入向量和标量的结果
      抛出:
      UnsupportedOperationException - 如果此向量不支持请求的操作
      IllegalArgumentException - 如果给定的long值无法由向量操作的右操作数类型表示
      参见:
    • lanewise

      public abstract Vector<E> lanewise(VectorOperators.Binary op, long e, VectorMask<E> m)
      将此向量的对应通道值与第二个输入向量的值组合,通过掩码控制通道元素的选择。这是一种逐通道二元操作,将所选操作应用于每个通道。第二个操作数是一个广播整数值。返回值将等于此表达式:this.lanewise(op, this.broadcast(e), m)
      API注释:
      longe必须能够准确表示为此向量种类的ETYPE,以便e==(long)(ETYPE)e。此规则由对broadcast()的隐式调用强制执行。

      子类型通过提高方法返回类型和标量参数e的类型来改进此方法。

      参数:
      op - 用于组合通道值的操作
      e - 输入标量
      m - 控制通道选择的掩码
      返回:
      将操作逐通道应用于输入向量和标量的结果
      抛出:
      UnsupportedOperationException - 如果此向量不支持请求的操作
      IllegalArgumentException - 如果给定的long值无法由向量操作的右操作数类型表示
      参见:
    • lanewise

      public abstract Vector<E> lanewise(VectorOperators.Ternary op, Vector<E> v1, Vector<E> v2)
      将此向量的对应通道值与第二个和第三个输入向量的通道值组合。这是一种逐通道三元操作,将所选操作应用于每个通道。
      API注释:
      子类型通过提高方法返回类型来改进此方法。
      参数:
      op - 用于组合通道值的操作
      v1 - 第二个输入向量
      v2 - 第三个输入向量
      返回:
      将操作逐通道应用于三个输入向量的结果
      抛出:
      UnsupportedOperationException - 如果此向量不支持请求的操作
      参见:
    • lanewise

      public abstract Vector<E> lanewise(VectorOperators.Ternary op, Vector<E> v1, Vector<E> v2, VectorMask<E> m)
      将此向量的对应通道值与第二个和第三个输入向量的通道值组合,通过掩码控制通道元素的选择。这是一种逐通道三元操作,将所选操作应用于每个通道。
      API注释:
      子类型通过提高方法返回类型来改进此方法。
      参数:
      op - 用于组合通道值的操作
      v1 - 第二个输入向量
      v2 - 第三个输入向量
      m - 控制通道选择的掩码
      返回:
      将操作逐通道应用于三个输入向量的结果
      抛出:
      UnsupportedOperationException - 如果此向量不支持请求的操作
      参见:
    • add

      public abstract Vector<E> add(Vector<E> v)
      将此向量添加到第二个输入向量。这是一种逐通道二元操作,将原始加法操作(+)应用于每对对应的通道值。此方法还等同于表达式lanewise(ADD, v)

      作为一个全功能命名操作,此方法有掩码和非掩码的重载,并且(在子类中)还有标量广播的重载(掩码和非掩码)。

      参数:
      v - 第二个输入向量
      返回:
      将此向量添加到第二个输入向量的结果
      参见:
    • add

      public abstract Vector<E> add(Vector<E> v, VectorMask<E> m)
      将此向量添加到第二个输入向量,根据掩码控制通道选择。这是一种带掩码的逐通道二元操作,将原始加法操作(+)应用于每对对应的通道值。对于掩码中未设置的任何通道,原始操作将被抑制,此向量保留该通道中存储的原始值。此方法还等同于表达式lanewise(ADD, v, m)

      作为一个全功能命名操作,此方法有掩码和非掩码的重载,并且(在子类中)还有标量广播的重载(掩码和非掩码)。

      参数:
      v - 第二个输入向量
      m - 控制通道选择的掩码
      返回:
      将此向量添加到给定向量的结果
      参见:
    • sub

      public abstract Vector<E> sub(Vector<E> v)
      从此向量中减去第二个输入向量。这是一种逐个通道的二进制操作,对每对对应通道值应用原始减法操作(-)。此方法也等同于表达式lanewise(SUB, v)

      作为一个全服务命名操作,此方法有掩码和非掩码的重载,并且(在子类中)还有标量广播的重载(包括掩码和非掩码)。

      参数:
      v - 第二个输入向量
      返回:
      减去第二个输入向量后的结果
      参见:
    • sub

      public abstract Vector<E> sub(Vector<E> v, VectorMask<E> m)
      在掩码的控制下,从此向量中减去第二个输入向量。这是一种掩码的逐个通道的二进制操作,对每对对应通道值应用原始减法操作(-)。对于掩码中未设置的通道,原始操作被抑制,此向量保留该通道中存储的原始值。此方法也等同于表达式lanewise(SUB, v, m)

      作为一个全服务命名操作,此方法有掩码和非掩码的重载,并且(在子类中)还有标量广播的重载(包括掩码和非掩码)。

      参数:
      v - 第二个输入向量
      m - 控制通道选择的掩码
      返回:
      减去第二个输入向量后的结果
      参见:
    • mul

      public abstract Vector<E> mul(Vector<E> v)
      将此向量乘以第二个输入向量。这是一种逐个通道的二进制操作,对每对对应通道值应用原始乘法操作(*)。此方法也等同于表达式lanewise(MUL, v)

      作为一个全服务命名操作,此方法有掩码和非掩码的重载,并且(在子类中)还有标量广播的重载(包括掩码和非掩码)。

      参数:
      v - 第二个输入向量
      返回:
      将此向量乘以第二个输入向量的结果
      参见:
    • mul

      public abstract Vector<E> mul(Vector<E> v, VectorMask<E> m)
      在掩码的控制下,将此向量乘以第二个输入向量。这是一种逐个通道的二进制操作,对每对对应通道值应用原始乘法操作(*)。对于掩码中未设置的通道,原始操作被抑制,此向量保留该通道中存储的原始值。此方法也等同于表达式lanewise(MUL, v, m)

      作为一个全服务命名操作,此方法有掩码和非掩码的重载,并且(在子类中)还有标量广播的重载(包括掩码和非掩码)。

      参数:
      v - 第二个输入向量
      m - 控制通道选择的掩码
      返回:
      将此向量乘以给定向量的结果
      参见:
    • div

      public abstract Vector<E> div(Vector<E> v)
      将此向量除以第二个输入向量。这是一种逐个通道的二进制操作,对每对对应通道值应用原始除法操作(/)。此方法也等同于表达式lanewise(DIV, v)

      作为一个全服务命名操作,此方法有掩码和非掩码的重载,并且(在子类中)还有标量广播的重载(包括掩码和非掩码)。

      API 注意:
      如果基础标量运算符不支持除以零,但提供了零除数,则将抛出ArithmeticException
      参数:
      v - 第二个输入向量
      返回:
      将此向量除以第二个输入向量的结果
      抛出:
      ArithmeticException - 如果v中的任何通道为零且ETYPE不是floatdouble
      参见:
    • div

      public abstract Vector<E> div(Vector<E> v, VectorMask<E> m)
      在掩码的控制下,将此向量除以第二个输入向量。这是一种逐个通道的二进制操作,对每对对应通道值应用原始除法操作(/)。对于掩码中未设置的通道,原始操作被抑制,此向量保留该通道中存储的原始值。此方法也等同于表达式lanewise(DIV, v, m)

      作为一个全服务命名操作,此方法有掩码和非掩码的重载,并且(在子类中)还有标量广播的重载(包括掩码和非掩码)。

      API 注意:
      如果基础标量运算符不支持除以零,但提供了零除数,则将抛出ArithmeticException
      参数:
      v - 第二个输入向量
      m - 控制通道选择的掩码
      返回:
      将此向量除以第二个输入向量的结果
      抛出:
      ArithmeticException - 如果m选择的v中的任何通道为零且ETYPE不是floatdouble
      参见:
    • neg

      public abstract Vector<E> neg()
      取反此向量。这是一种逐个通道的一元操作,对每个输入通道应用原始取反操作(-x)。此方法也等同于表达式lanewise(NEG)
      API注释:
      此方法没有掩码变体,但相应的掩码操作可以从lanewise方法中获取。
      返回:
      此向量的否定值
      参见:
    • abs

      public abstract Vector<E> abs()
      返回此向量的绝对值。这是一个逐个通道的一元操作,它对每个输入通道应用方法Math.abs。此方法也等同于表达式lanewise(ABS)
      API注释:
      此方法没有掩码变体,但相应的掩码操作可以从lanewise方法中获取。
      返回:
      此向量的绝对值
      参见:
    • min

      public abstract Vector<E> min(Vector<E> v)
      计算此向量和第二个输入向量中较小的值。这是一个逐个通道的二元操作,它对应的通道值对应应用操作Math.min()。此方法也等同于表达式lanewise(MIN, v)
      API注释:
      这不是像add()那样的全功能命名操作。此操作的掩码版本不直接可用,但可以通过lanewise的掩码版本获得。子类定义了此方法的额外标量广播重载。
      参数:
      v - 第二个输入向量
      返回:
      此向量和第二个输入向量的逐个通道最小值
      参见:
    • max

      public abstract Vector<E> max(Vector<E> v)
      计算此向量和第二个输入向量中较大的值。这是一个逐个通道的二元操作,它对应的通道值对应应用操作Math.max()。此方法也等同于表达式lanewise(MAX, v)

      这不是像add()那样的全功能命名操作。此操作的掩码版本不直接可用,但可以通过lanewise的掩码版本获得。子类定义了此方法的额外标量广播重载。

      参数:
      v - 第二个输入向量
      返回:
      此向量和第二个输入向量的逐个通道最大值
      参见:
    • reduceLanesToLong

      public abstract long reduceLanesToLong(VectorOperators.Associative op)
      返回从此向量的所有通道累积的值。这是一个关联的跨通道归约操作,它将指定的操作应用于所有通道元素。返回值将等于此表达式:(long) ((EVector)this).reduceLanes(op),其中EVector是特定于此向量元素类型ETYPE的向量类。

      对于操作ADDMUL,当ETYPEfloatdouble时,精确结果,在进行转换之前,将反映对操作的任意操作顺序的选择,甚至可能随时间变化。有关更多详细信息,请参阅浮点向量上的操作部分。

      API注释:
      如果ETYPEfloatdouble,此操作可能会丢失精度和/或范围,因为将结果向下转换为long是正常的部分。通常,如果您正在使用具有已知元素类型的向量子类型,则更倾向于使用强类型访问
      参数:
      op - 用于组合通道值的操作
      返回:
      累积结果,转换为long
      抛出:
      UnsupportedOperationException - 如果此向量不支持请求的操作
      参见:
    • reduceLanesToLong

      public abstract long reduceLanesToLong(VectorOperators.Associative op, VectorMask<E> m)
      返回从此向量的选定通道累积的值,由掩码控制。这是一个关联的跨通道归约操作,它将指定的操作应用于选定的通道元素。返回值将等于此表达式:(long) ((EVector)this).reduceLanes(op, m),其中EVector是特定于此向量元素类型ETYPE的向量类。

      如果没有选择元素,则返回特定于操作的身份值。

      • 如果操作是ADDXOROR,则身份值为零。
      • 如果操作是MUL,则身份值为一。
      • 如果操作是AND,则身份值为负一(所有位设置)。
      • 如果操作是MAX,则身份值为向量的本机ETYPEMIN_VALUE。(对于浮点类型,使用值NEGATIVE_INFINITY,并在转换后显示为Long.MIN_VALUE
      • 如果操作是MIN,则身份值为向量的本机ETYPEMAX_VALUE。(对于浮点类型,使用值POSITIVE_INFINITY,并在转换后显示为Long.MAX_VALUE

      对于操作ADDMUL,当ETYPEfloatdouble时,精确结果,在进行转换之前,将反映对操作的任意操作顺序的选择,甚至可能随时间变化。有关更多详细信息,请参阅浮点向量上的操作部分。

      API注释:
      如果ETYPEfloatdouble,此操作可能会丢失精度和/或范围,因为将结果向下转换为long是正常的部分。通常,如果您正在使用具有已知元素类型的向量子类型,则更倾向于使用强类型访问
      参数:
      op - 用于组合通道值的操作
      m - 控制通道选择的掩码
      返回:
      从选定通道值累积的减少结果
      抛出:
      UnsupportedOperationException - 如果此向量不支持请求的操作
      参见:
    • test

      public abstract VectorMask<E> test(VectorOperators.Test op)
      根据给定操作测试此向量的通道。这是一个逐个通道的一元测试操作,它将给定的测试操作应用于每个通道值。
      参数:
      op - 用于测试通道值的操作
      返回:
      测试此向量通道的掩码结果,根据所选的测试运算符
      参见:
    • test

      public abstract VectorMask<E> test(VectorOperators.Test op, VectorMask<E> m)
      测试所选通道的此向量,根据给定的操作。这是一种掩码通道逐个测试操作,将给定的测试操作应用于每个通道值。返回的结果等于表达式test(op).and(m)
      参数:
      op - 用于测试通道值的操作
      m - 控制通道选择的掩码
      返回:
      测试此向量通道的掩码结果,根据所选的测试运算符,并且仅在掩码选择的通道中
      参见:
    • eq

      public abstract VectorMask<E> eq(Vector<E> v)
      测试此向量是否等于另一个输入向量。这是一种通道逐个二进制测试操作,将原始等式操作(==)应用于每对对应通道值。结果与compare(VectorOperators.EQ, v)相同。
      参数:
      v - 第二个输入向量
      返回:
      测试通道是否逐个等于第二个输入向量的掩码结果
      参见:
    • lt

      public abstract VectorMask<E> lt(Vector<E> v)
      测试此向量是否小于另一个输入向量。这是一种通道逐个二进制测试操作,将原始小于操作(<)应用于每个通道。结果与compare(VectorOperators.LT, v)相同。
      参数:
      v - 第二个输入向量
      返回:
      测试通道是否逐个小于第二个输入向量的掩码结果
      参见:
    • compare

      public abstract VectorMask<E> compare(VectorOperators.Comparison op, Vector<E> v)
      通过使用给定的比较操作比较它与另一个输入向量来测试此向量。这是一种通道逐个二进制测试操作,将给定的比较操作应用于每对对应通道值。
      参数:
      op - 用于比较通道值的操作
      v - 第二个输入向量
      返回:
      测试通道是否逐个与输入比较的掩码结果,根据所选的比较运算符
      参见:
    • compare

      public abstract VectorMask<E> compare(VectorOperators.Comparison op, Vector<E> v, VectorMask<E> m)
      通过使用给定的比较操作比较它与另一个输入向量来测试此向量,仅在由掩码选择的通道中。这是一种掩码通道逐个二进制测试操作,将给定的比较操作应用于每对对应通道值。返回的结果等于表达式compare(op,v).and(m)
      参数:
      op - 用于比较通道值的操作
      v - 第二个输入向量
      m - 控制通道选择的掩码
      返回:
      测试通道是否逐个与输入比较的掩码结果,根据所选的比较运算符,并且仅在由掩码选择的通道中
      参见:
    • compare

      public abstract VectorMask<E> compare(VectorOperators.Comparison op, long e)
      通过使用给定的比较操作将其与输入标量进行比较来测试此向量。这是一种通道逐个二进制测试操作,将给定的比较操作应用于每个通道值,与广播值配对。

      结果与this.compare(op, this.broadcast(e))相同。也就是说,标量可以被视为广播到相同种类的向量,然后使用所选的比较操作与原始向量进行比较。

      API 注意:
      longe 必须能够准确表示此向量种类的 ETYPE,以便 e==(long)(ETYPE)e。此规则由对 broadcast() 的隐式调用强制执行。

      子类型通过提高标量参数 e 的类型来改进此方法。

      参数:
      op - 用于比较通道值的操作
      e - 输入标量
      返回:
      测试通道是否逐个与输入比较的掩码结果,根据所选的比较运算符
      抛出:
      IllegalArgumentException - 如果给定的 long 值无法由向量的 ETYPE 表示
      参见:
    • compare

      public abstract VectorMask<E> compare(VectorOperators.Comparison op, long e, VectorMask<E> m)
      通过使用给定的比较操作将其与输入标量进行比较来测试此向量,仅在由掩码选择的通道中。这是一种掩码通道逐个二进制测试操作,将给定的比较操作应用于每个通道值,与广播值配对。返回的结果等于表达式compare(op,e).and(m)
      API 注意:
      longe 必须能够准确表示此向量种类的 ETYPE,以便 e==(long)(ETYPE)e。此规则由对 broadcast() 的隐式调用强制执行。

      子类型通过提高标量参数 e 的类型来改进此方法。

      参数:
      op - 用于比较通道值的操作
      e - 输入标量
      m - 控制通道选择的掩码
      返回:
      测试通道是否逐个与输入比较的掩码结果,根据所选的比较运算符,并且仅在由掩码选择的通道中
      抛出:
      IllegalArgumentException - 如果给定的 long 值无法由向量的 ETYPE 表示
      参见:
    • blend

      public abstract Vector<E> blend(Vector<E> v, VectorMask<E> m)
      使用第二个输入向量中的相应通道替换此向量的选定通道,受掩码控制。这是一种掩码通道逐个二进制操作,从一个输入中选择每个通道值。
      • 对于掩码中的任何通道 设置,新的通道值取自第二个输入向量,并替换此向量的该通道中的任何值。
      • 对于掩码中的任何通道 未设置,将抑制替换,并且此向量保留该通道中存储的原始值。
      以下伪代码说明了此行为:
      
       Vector<E> a = ...;
       VectorSpecies<E> species = a.species();
       Vector<E> b = ...;
       b.check(species);
       VectorMask<E> m = ...;
       ETYPE[] ar = a.toArray();
       for (int i = 0; i < ar.length; i++) {
           if (m.laneIsSet(i)) {
               ar[i] = b.lane(i);
           }
       }
       return EVector.fromArray(s, ar, 0);
       
      参数:
      v - 第二个输入向量,包含替换通道值
      m - 控制从第二个输入向量中选择通道的掩码
      返回:
      将此向量的通道元素与第二个输入向量的通道元素混合的结果
    • blend

      public abstract Vector<E> blend(long e, VectorMask<E> m)
      使用标量值替换此向量的选定通道,受掩码控制。这是一种掩码通道逐个二进制操作,从一个输入中选择每个通道值。返回的结果等于表达式blend(broadcast(e),m)
      API 注意:
      longe 必须能够准确表示此向量种类的 ETYPE,以便 e==(long)(ETYPE)e。此规则由对 broadcast() 的隐式调用强制执行。

      子类型通过提高标量参数 e 的类型来改进此方法。

      参数:
      e - 输入标量,包含替换通道值
      m - 控制标量通道选择的掩码
      返回:
      将此向量的通道元素与标量值混合的结果
    • addIndex

      public abstract Vector<E> addIndex(int scale)
      将此向量的各个通道添加到其对应的通道编号中,乘以给定的常数。这是一种逐通道的一元操作,对于每个通道N,计算缩放后的索引值N*scale,并将其添加到当前向量的通道N中的值。

      缩放必须不要太大,元素大小也不要太小,以至于在使用向量通道类型ETYPE表示任何N*scaleVLENGTH*scale时会发生溢出。

      以下伪代码说明了这种行为:

      
       Vector<E> a = ...;
       VectorSpecies<E> species = a.species();
       ETYPE[] ar = a.toArray();
       for (int i = 0; i < ar.length; i++) {
           long d = (long)i * scale;
           if (d != (ETYPE) d)  throw ...;
           ar[i] += (ETYPE) d;
       }
       long d = (long)ar.length * scale;
       if (d != (ETYPE) d)  throw ...;
       return EVector.fromArray(s, ar, 0);
       
      参数:
      scale - 用于乘以每个通道索引N的数字,通常为1
      返回:
      将每个通道元素递增其对应的通道索引N,乘以scale的结果
      抛出:
      IllegalArgumentException - 如果区间[0..VLENGTH*scale]中的值不能由ETYPE表示
    • slice

      public abstract Vector<E> slice(int origin, Vector<E> v1)
      从当前向量的给定origin通道开始,切片相邻通道的段,继续(如有必要)到紧随其后的另一个向量。将VLENGTH个通道的块提取到自己的向量中并返回。

      这是一种跨通道操作,将通道元素从当前向量和第二个向量移至前端。这两个向量可以被视为长度为2*VLENGTH的组合“背景”,从中提取一个切片。输出向量中编号为N的通道是从输入向量的通道origin+N复制而来,如果该通道存在,则从第二个向量的通道origin+N-VLENGTH复制而来(保证存在)。

      origin值必须在包含范围0..VLENGTH内。作为极端情况,v.slice(0,w)v.slice(VLENGTH,w)分别返回vw

      API注释:
      这个方法可以被视为unslice()的逆操作,因为切片值可以被无损地还原到两个输入向量中的原始位置,而不会影响不相关的元素,如下伪代码所示:
      
       EVector slice = v1.slice(origin, v2);
       EVector w1 = slice.unslice(origin, v1, 0);
       EVector w2 = slice.unslice(origin, v2, 1);
       assert v1.equals(w1);
       assert v2.equals(w2);
       

      此方法还支持各种跨通道移位和旋转,如下所示:

      • 要将通道向前移至向量前端,请为第二个操作数提供一个零向量,并将移位计数指定为起始位置。例如:v.slice(shift, v.broadcast(0))
      • 要将通道向后移至向量后端,请为第一个操作数提供一个零向量,并将负移位计数指定为起始位置(模VLENGTH)。例如:v.broadcast(0).slice(v.length()-shift, v)
      • 要将通道向前旋转至向量前端,将最早的通道循环到后端,为两个操作数提供相同的向量,并将旋转计数指定为起始位置。例如:v.slice(rotate, v)
      • 要将通道向后旋转至向量后端,将最新的通道循环到前端,为两个操作数提供相同的向量,并将负旋转计数(模VLENGTH)指定为起始位置。例如:v.slice(v.length() - rotate, v)
      • 由于origin值小于零或大于VLENGTH将被拒绝,如果需要以不可预测的VLENGTH倍数旋转,请确保将起始值减少到所需范围内。loopBound()方法可以帮助实现这一点。例如:v.slice(rotate - v.species().loopBound(rotate), v)
      参数:
      origin - 要传输到切片中的第一个输入通道
      v1 - 在切片被取出之前,逻辑上与第一个向量连接的第二个向量(如果省略,默认为零)
      返回:
      从此向量开始的指定起始位置处,连续切片VLENGTH个通道,继续(如有必要)到第二个向量
      抛出:
      ArrayIndexOutOfBoundsException - 如果origin为负或大于VLENGTH
      参见:
    • slice

      public abstract Vector<E> slice(int origin, Vector<E> v1, VectorMask<E> m)
      在掩码的控制下切片相邻通道的段,从当前向量的给定origin通道开始,继续(如有必要)到紧随其后的另一个向量。将VLENGTH个通道的块提取到自己的向量中并返回。结果向量中未设置的通道将为零。在掩码中设置的通道将包含从所选通道的thisv1复制的数据。

      这是一种跨通道操作,将通道元素从当前向量和第二个向量移至前端。这两个向量可以被视为长度为2*VLENGTH的组合“背景”,从中提取一个切片。返回的结果等于表达式broadcast(0).blend(slice(origin,v1),m)

      API注释:
      这个方法可以被视为#unslice(int,Vector,int,VectorMask) unslice()的逆操作,因为切片值可以被无损地还原到两个输入向量中的原始位置,而不会影响不相关的元素,如下伪代码所示:
      
       EVector slice = v1.slice(origin, v2, m);
       EVector w1 = slice.unslice(origin, v1, 0, m);
       EVector w2 = slice.unslice(origin, v2, 1, m);
       assert v1.equals(w1);
       assert v2.equals(w2);
       
      参数:
      origin - 要传输到切片中的第一个输入通道
      v1 - 在切片被取出之前,逻辑上与第一个向量连接的第二个向量(如果省略,默认为零)
      m - 控制通道选择进入结果向量的掩码
      返回:
      从此向量开始的指定起始位置处,连续切片VLENGTH个通道,继续(如有必要)到第二个向量
      抛出:
      ArrayIndexOutOfBoundsException - 如果origin为负或大于VLENGTH
      参见:
    • slice

      public abstract Vector<E> slice(int origin)
      在当前向量中的给定origin通道开始切片相邻通道的段。将一个由VLENGTH个通道组成的块,可能填充有零通道,提取到自己的向量中并返回。这是一个方便的方法,它从一个单一向量中切片,对比一个由零通道组成的扩展背景。它等同于slice (origin, broadcast(0))。也可以简单地将其视为从后向前通道的跨通道移位,用零填充在向量末尾的空通道。在这种观点下,移位计数为origin
      参数:
      origin - 要传输到切片中的第一个输入通道
      返回:
      最后的VLENGTH-origin个输入通道,从输出的第一个通道开始放置,末尾填充为零
      抛出:
      ArrayIndexOutOfBoundsException - 如果origin为负或大于VLENGTH
      参见:
    • unslice

      public abstract Vector<E> unslice(int origin, Vector<E> w, int part)
      反转一个slice(),将当前向量作为一个切片插入到另一个“背景”输入向量中,该向量被视为假设后续slice()操作的一个或另一个输入。

      这是一种跨通道操作,将当前向量的通道元素向后排列,并将它们插入到逻辑背景向量对中。然而,只返回其中一个。背景由复制第二个输入向量形成。(但是,输出永远不会包含来自相同输入通道的两个重复项。)输入向量中编号为N的通道被复制到第一个背景向量的通道origin+N中,如果该通道存在,则复制到第二个背景向量的通道origin+N-VLENGTH中(保证存在)。更新后插入切片的第一个或第二个背景向量将被返回。零或一的part数字选择第一个或第二个更新后的背景向量。

      origin值必须在包含范围0..VLENGTH内。作为极端情况,v.unslice(0,w,0)v.unslice(VLENGTH,w,1)都返回v,而v.unslice(0,w,1)v.unslice(VLENGTH,w,0)都返回w

      API注释:
      此方法支持各种跨通道插入操作,如下所示:
      • 要在背景向量w的末尾附近的某个偏移处插入,请将偏移指定为原点,并选择零部分。例如:v.unslice(offset, w, 0)
      • 要在背景向量w的末尾附近插入,但捕获溢出到下一个向量x,请将偏移指定为原点,并选择部分一。例如:v.unslice(offset, x, 1)
      • 要在背景向量w的开头附近插入最后的N项,请将VLENGTH-N作为原点,并选择部分一。例如:v.unslice(v.length()-N, w)
      参数:
      origin - 接收切片的第一个输出通道
      w - 将接收插入切片的背景向量(作为两个副本)
      part - 结果的部分编号(零或一)
      返回:
      通过在指定原点插入此向量而更新的背景向量w的一部分或两部分
      抛出:
      ArrayIndexOutOfBoundsException - 如果origin为负或大于VLENGTH,或者如果part不是零或一
      参见:
    • unslice

      public abstract Vector<E> unslice(int origin, Vector<E> w, int part, VectorMask<E> m)
      反转slice(),根据掩码的控制,在另一个“背景”输入向量中将当前向量作为切片插入,这被视为假设的后续slice()操作的一个输入。

      这是一个跨通道操作,它重新排列当前向量的通道元素并将其通道(由掩码选择)插入到逻辑背景向量的一对中。与此方法的未掩码版本一样,只有一对中的一个将被返回,由part编号选择。对于掩码选择的每个通道N,如果存在该通道,则将通道值复制到第一个背景向量的通道origin+N中,否则将通道值复制到第二个背景向量的通道origin+N-VLENGTH中(保证存在)。如果掩码中对应的输入通道N未设置,则背景通道保留其原始值。更新了插入切片的设置通道的第一个或第二个背景向量将被返回。部分编号为零或一选择第一个或第二个更新的背景向量。

      参数:
      origin - 接收切片的第一个输出通道
      w - 将接收插入切片的背景向量(作为两个副本),如果在m中设置了它们
      part - 结果的部分编号(零或一)
      m - 控制从当前向量选择通道的掩码
      返回:
      通过在指定原点插入此向量的选定通道而更新的背景向量w的一部分或两部分
      抛出:
      ArrayIndexOutOfBoundsException - 如果origin为负或大于VLENGTH,或者如果part不是零或一
      参见:
    • unslice

      public abstract Vector<E> unslice(int origin)
      反转slice(),将当前向量作为切片插入到值为零的“背景”输入中。与其他unslice()方法相比,此方法仅返回一对背景向量中的第一个。这是一个便利方法,它返回unslice(origin,broadcast(0),0)的结果。它也可以简单地被视为从早期到后期通道的跨通道移位,零填充在向量开头的空通道中。在这种观点下,移位计数为origin
      参数:
      origin - 接收切片的第一个输出通道
      返回:
      从给定原点开始放置的第一个VLENGTH-origin输入通道,开头填充为零
      抛出:
      ArrayIndexOutOfBoundsException - 如果origin为负或大于VLENGTH
      参见:
    • rearrange

      public abstract Vector<E> rearrange(VectorShuffle<E> s)
      选择受特定洗牌控制的通道,重新排列此向量的通道元素。这是一个跨通道操作,重新排列此向量的通道元素。对于洗牌的每个通道N,对于洗牌中的每个通道源索引I=s.laneSource(N),输出通道N从输入向量的通道I获取值。
      参数:
      s - 控制通道索引选择的洗牌
      返回:
      重新排列此向量的通道元素
      抛出:
      IndexOutOfBoundsException - 如果洗牌中存在任何异常源索引
      参见:
    • rearrange

      public abstract Vector<E> rearrange(VectorShuffle<E> s, VectorMask<E> m)
      选择受特定洗牌和掩码控制的通道,重新排列此向量的通道元素。这是一个跨通道操作,重新排列此向量的通道元素。对于洗牌的每个通道N,对于洗牌中的每个通道源索引I=s.laneSource(N),如果掩码已设置,则输出通道N从输入向量的通道I获取值。否则,如果掩码未设置,则输出通道N设置为零。

      此方法返回以下伪代码的值:

      
       Vector<E> r = this.rearrange(s.wrapIndexes());
       VectorMask<E> valid = s.laneIsValid();
       if (m.andNot(valid).anyTrue()) throw ...;
       return broadcast(0).blend(r, m);
       
      参数:
      s - 控制从当前向量和第二个输入向量选择通道的洗牌
      m - 控制应用洗牌的掩码
      返回:
      重新排列此向量的通道元素
      抛出:
      IndexOutOfBoundsException - 如果洗牌中存在任何异常源索引,并且掩码已设置
      参见:
    • rearrange

      public abstract Vector<E> rearrange(VectorShuffle<E> s, Vector<E> v)
      选择受特定洗牌控制的通道,使用洗牌中的正常和异常索引来引导数据,重新排列两个向量的通道元素(当前向量和第二个向量v)。对于洗牌的每个通道N,对于洗牌中的每个通道源索引I=s.laneSource(N),如果I>=0,输出通道N从第一个向量的通道I获取值。否则,异常索引I通过添加VLENGTH进行包装,并用于索引第二个向量,索引为I+VLENGTH

      此方法返回以下伪代码的值:

      
       Vector<E> r1 = this.rearrange(s.wrapIndexes());
       // 或者:r1 = this.rearrange(s, s.laneIsValid());
       Vector<E> r2 = v.rearrange(s.wrapIndexes());
       return r2.blend(r1,s.laneIsValid());
       
      参数:
      s - 控制从两个输入向量选择通道的洗牌
      v - 第二个输入向量
      返回:
      重新排列此向量和第二个输入向量的通道元素
      参见:
    • compress

      public abstract Vector<E> compress(VectorMask<E> m)
      选择受特定掩码控制的通道,压缩此向量的通道元素。这是一个跨通道操作,根据指定的掩码选择,压缩此向量的通道元素。对于掩码的每个通道N,如果通道N处的掩码已设置,则从输入向量的通道N选择元素并连续存储到从通道0开始的输出向量中。如果有的话,输出向量的所有上部剩余通道将设置为零。
      参数:
      m - 控制压缩的掩码
      返回:
      此向量的压缩通道元素
      自:
      19
    • expand

      public abstract Vector<E> expand(VectorMask<E> m)
      在特定掩码的控制下,扩展此向量的车道元素。这是一个跨车道操作,根据指定的掩码将此向量的连续车道元素扩展为输出向量的车道,选择的方式由指定的掩码选择。对于掩码的每个车道N,如果车道N处的掩码被设置,则从车道0开始选择输入向量的下一个连续元素,并将其存储到输出向量的车道N中。所有剩余的车道(如果有)的输出向量都设置为零。
      参数:
      m - 控制压缩的掩码
      返回:
      此向量的扩展车道元素
      自:
      19
    • selectFrom

      public abstract Vector<E> selectFrom(Vector<E> v)
      使用存储在此向量的车道中的索引值,组装存储在第二个向量v中的值。因此,第二个向量充当一个表,其中的元素由当前向量中的索引选择。这是一个跨车道操作,根据此向量的控制重新排列参数向量的车道元素。对于此向量的每个车道N,以及此向量中此车道值I=this.lane(N),输出车道N获取来自参数向量在车道I处的值。通过这种方式,结果仅包含存储在参数向量v中的值,但是以取决于this中的索引值的顺序呈现。结果与表达式v.rearrange(this.toShuffle())相同。
      参数:
      v - 提供结果值的向量
      返回:
      v的车道元素的重新排列
      抛出:
      IndexOutOfBoundsException - 如果在this中找到任何无效的源索引
      参见:
    • selectFrom

      public abstract Vector<E> selectFrom(Vector<E> v, VectorMask<E> m)
      使用存储在此向量的车道中的索引值,根据掩码组装存储在第二个向量中的值。因此,第二个向量充当一个表,其中的元素由当前向量中的索引选择。在掩码中未设置的车道接收零而不是来自表的值。这是一个跨车道操作,根据此向量和掩码的控制重新排列参数向量的车道元素。结果与表达式v.rearrange(this.toShuffle(), m)相同。
      参数:
      v - 提供结果值的向量
      m - 控制从v中选择的掩码
      返回:
      v的车道元素的重新排列
      抛出:
      IndexOutOfBoundsException - 如果在this中找到任何无效的源索引,并且在掩码中设置了车道
      参见:
    • broadcast

      public abstract Vector<E> broadcast(long e)
      返回与此向量相同类型的向量,其中所有车道元素均设置为基本值e。当前向量的内容被丢弃;只有种类对此操作是相关的。

      此方法返回以下表达式的值:EVector.broadcast(this.species(), (ETYPE)e),其中EVector是特定于此向量元素类型ETYPE的向量类。

      必须能够准确表示ETYPElonge,以便e==(long)(ETYPE)e。如果违反此规则,问题不会在静态时被检测到,但在运行时会抛出IllegalArgumentException。因此,此方法在一定程度上削弱了立即常量和其他标量的静态类型检查,但通过提高通用API的表现力来弥补这一点。请注意,范围在[-128..127]e值始终是可接受的,因为每个ETYPE都将接受每个byte值。

      API注释:
      子类型通过提高方法返回类型和标量参数e的类型来改进此方法。
      参数:
      e - 要广播的值
      返回:
      所有车道元素均设置为基本值e的向量
      抛出:
      IllegalArgumentException - 如果给定的long值无法由向量的ETYPE表示
      参见:
    • maskAll

      public abstract VectorMask<E> maskAll(boolean bit)
      返回与此向量相同类型的掩码,其中每个车道根据给定的单个布尔值设置或取消设置,该值被广播到所有车道。

      此方法返回以下表达式的值:species().maskAll(bit)

      参数:
      bit - 要复制的给定掩码位
      返回:
      每个车道根据给定位设置或取消设置的掩码
      参见:
    • toShuffle

      public abstract VectorShuffle<E> toShuffle()
      将此向量转换为一个洗牌,将车道值转换为int并将其视为源索引。

      此方法的行为就好像返回给定向量元素数组创建洗牌的结果一样,如下所示:

      
       long[] a = this.toLongArray();
       int[] sa = new int[a.length];
       for (int i = 0; i < a.length; i++) {
           sa[i] = (int) a[i];
       }
       return VectorShuffle.fromValues(this.species(), sa);
       
      返回:
      此向量的洗牌表示
      参见:
    • reinterpretShape

      public abstract <F> Vector<F> reinterpretShape(VectorSpecies<F> species, int part)
      将此向量转换为给定元素类型F的种类的向量,重新解释此向量的字节而不执行任何值转换。

      根据所选的种类,此操作可能会扩展或收缩其逻辑结果,此时非零的part号可以进一步控制逻辑结果的选择和引导到物理输出向量中。

      此向量的基础位将被复制到结果向量中而不进行修改,但在复制之前,如果此向量的位大小大于所需向量的位大小,则这些位可能会被截断,或者如果此向量的位大小小于所需向量的位大小,则这些位可能会被填充为零位。

      如果旧种类和新种类具有不同的形状,则这是一个改变形状的操作,并且可能具有特殊的实现成本。

      该方法的行为就好像将此向量存储到字节数组中,使用小端字节顺序,然后从相同的字节数组中使用相同的顺序加载所需的向量。

      以下伪代码说明了行为:

      
       int domSize = this.byteSize();
       int ranSize = species.vectorByteSize();
       int M = (domSize > ranSize ? domSize / ranSize : ranSize / domSize);
       assert Math.abs(part) < M;
       assert (part == 0) || (part > 0) == (domSize > ranSize);
       MemorySegment ms = MemorySegment.ofArray(new byte[Math.max(domSize, ranSize)]);
       if (domSize > ranSize) {  // expansion
           this.intoMemorySegment(ms, 0, ByteOrder.native());
           int origin = part * ranSize;
           return species.fromMemorySegment(ms, origin, ByteOrder.native());
       } else {  // contraction or size-invariant
           int origin = (-part) * domSize;
           this.intoMemorySegment(ms, origin, ByteOrder.native());
           return species.fromMemorySegment(ms, 0, ByteOrder.native());
       }
       
      API注释:
      尽管此方法被定义为将相关向量加载或存储到内存中,但内存语义与实际实现几乎没有关系。对小端序的引用只是一个简写,否则可能会涉及大量关于车道结构向量和字节结构向量之间映射的详细规则。
      类型参数:
      F - 种类的装箱元素类型
      参数:
      species - 所需的向量种类
      part - 结果的部分号,如果既不扩展也不收缩,则为零
      返回:
      从此向量转换的,按形状和元素类型的向量
      参见:
    • reinterpretAsBytes

      public abstract ByteVector reinterpretAsBytes()
      将此向量视为具有相同形状和内容但车道类型为byte的向量,其中字节根据小端序从车道中提取。这是一个方便的方法,用于表达式reinterpretShape(species().withLanes(byte.class))。它可以被认为是将字节在同一向量中的车道中合并为更大车道的各种方法的逆操作,例如reinterpretAsInts()
      返回:
      一个具有相同形状和信息内容的ByteVector
      参见:
    • reinterpretAsShorts

      public abstract ShortVector reinterpretAsShorts()
      将此向量重新解释为具有相同形状和内容但是车道类型为short的向量,其中车道按照小端顺序从连续字节组装而成。这是一个方便的方法,等同于表达式reinterpretShape(species().withLanes(short.class))。可以认为它是reinterpretAsBytes()的逆操作。
      返回:
      一个具有相同形状和信息内容的ShortVector
    • reinterpretAsInts

      public abstract IntVector reinterpretAsInts()
      将此向量重新解释为具有相同形状和内容但是车道类型为int的向量,其中车道按照小端顺序从连续字节组装而成。这是一个方便的方法,等同于表达式reinterpretShape(species().withLanes(int.class))。可以认为它是reinterpretAsBytes()的逆操作。
      返回:
      一个具有相同形状和信息内容的IntVector
    • reinterpretAsLongs

      public abstract LongVector reinterpretAsLongs()
      将此向量重新解释为具有相同形状和内容但是车道类型为long的向量,其中车道按照小端顺序从连续字节组装而成。这是一个方便的方法,等同于表达式reinterpretShape(species().withLanes(long.class))。可以认为它是reinterpretAsBytes()的逆操作。
      返回:
      一个具有相同形状和信息内容的LongVector
    • reinterpretAsFloats

      public abstract FloatVector reinterpretAsFloats()
      将此向量重新解释为具有相同形状和内容但是车道类型为float的向量,其中车道按照小端顺序从连续字节组装而成。这是一个方便的方法,等同于表达式reinterpretShape(species().withLanes(float.class))。可以认为它是reinterpretAsBytes()的逆操作。
      返回:
      一个具有相同形状和信息内容的FloatVector
    • reinterpretAsDoubles

      public abstract DoubleVector reinterpretAsDoubles()
      将此向量重新解释为具有相同形状和内容但是车道类型为double的向量,其中车道按照小端顺序从连续字节组装而成。这是一个方便的方法,等同于表达式reinterpretShape(species().withLanes(double.class))。可以认为它是reinterpretAsBytes()的逆操作。
      返回:
      一个具有相同形状和信息内容的DoubleVector
    • viewAsIntegralLanes

      public abstract Vector<?> viewAsIntegralLanes()
      将此向量视为具有相同形状、长度和内容但不是浮点类型的向量。这是对车道值进行的逐车道重新解释转换。因此,此方法不会更改VSHAPEVLENGTH,向量的位内容也不会改变。如果向量的ETYPE已经是整数类型,则返回相同的向量。此方法返回以下表达式的值:convert(conv,0),其中convVectorOperators.Conversion.ofReinterpret(E.class,F.class)F是与E大小相同的非浮点类型。
      API注释:
      子类型通过提升返回类型来改进此方法。
      返回:
      重新解释为非浮点数的原始向量
      参见:
    • viewAsFloatingLanes

      public abstract Vector<?> viewAsFloatingLanes()
      将此向量视为具有相同形状、长度和内容但是浮点类型的向量。这是对车道值进行的逐车道重新解释转换。因此,此方法不会更改VSHAPEVLENGTH,向量的位内容也不会改变。如果向量的ETYPE已经是浮点类型,则返回相同的向量。如果向量的元素大小与任何浮点类型大小不匹配,则会抛出IllegalArgumentException。此方法返回以下表达式的值:convert(conv,0),其中convVectorOperators.Conversion.ofReinterpret(E.class,F.class)F是与E大小相同的浮点类型(如果有的话)。
      API注释:
      子类型通过提升返回类型来改进此方法。
      返回:
      重新解释为浮点数的原始向量
      抛出:
      UnsupportedOperationException - 如果没有与此向量的车道大小相同的浮点类型
      参见:
    • convert

      public abstract <F> Vector<F> convert(VectorOperators.Conversion<E,F> conv, int part)
      将此向量转换为具有相同形状和新元素类型的向量,根据指定的转换将当前ETYPE的车道值转换为新的车道类型(此处称为FTYPE)。这是一个逐车道形状不变的操作,将输入向量中的ETYPE值复制到结果中对应的FTYPE值。根据所选的转换,此操作可能会扩展或收缩其逻辑结果,在这种情况下,非零的part数字可以进一步控制将逻辑结果选择和引导到物理输出向量中。

      每个特定的转换在类VectorOperators中由一个转换常量描述。每个转换操作符都有指定的域类型范围类型。域类型必须与输入向量的车道类型完全匹配,而范围类型确定输出向量的车道类型。

      转换操作符可以根据其域类型的位大小是否等于、小于或大于其范围类型的位大小,分为原位、扩展或收缩三类。

      独立地,转换操作还可以根据转换是重新解释还是值转换来分类,取决于转换是否保持表示位不变,或者更改表示位以保留(部分或全部)输入值的逻辑值。

      如果重新解释的转换收缩,它将截断输入的高位。如果扩展,当没有对应的输入位时,它将用零位填充输出的高位。

      S2Ishort值到int)这样的扩展转换会将标量值表示为更大的格式(始终带有一些信息冗余)。像D2Fdouble值到float)这样的收缩转换会将标量值表示为更小的格式(始终带有一些信息丢失)。一些原位转换也可能包含信息丢失,例如L2Dlong值到double)或F2Ifloat值到int)。重新解释的原位转换不会丢失信息,除非比特值在某种程度上在输出类型中不合法。转换NaN的位模式可能会丢弃NaN的有效数字中的位。

      这种分类很重要,因为除非另有说明,转换操作永远不会改变向量形状,无论它们如何改变车道大小。因此,一个扩展转换不能将其所有结果存储在其输出向量中,因为输出向量具有更少的车道,但车道更大,以便具有与输入相同的总位大小。同样,一个收缩转换必须将其相对较小的结果存储到输出向量的一部分车道中,并将未使用的车道默认为零。

      例如,从bytelong的转换(M=8)将丢弃87.5%的输入值,以将剩余的12.5%转换为宽敞的long车道。逆转换将转换回所有大的结果,但将浪费输出向量中87.5%的车道。原位转换(M=1)将在一个输出向量中提供所有结果,而不浪费车道。

      为了管理这些扩展和收缩的细节,一个非零的part参数选择扩展的部分结果,或将收缩的结果引导到相应的位置,如下所示:

      • 通过M扩展:part必须在范围[0..M-1]内,并选择从原始车道开始的VLENGTH/M输入车道块。

        VLENGTH/M输出车道代表转换的整体逻辑结果的部分切片,填充整个物理输出向量。

      • 通过M收缩:part必须在范围[-M+1..0]内,并将所有VLENGTH输入车道引导到位于原始车道-part*VLENGTH的输出中。总共有VLENGTH*M输出车道,那些不持有转换后的输入值的车道将被填充为零。

        一组这样的输出向量,将逻辑结果部分引导到不相交的块中,可以使用按位或或(对于浮点数)FIRST_NONZERO操作符重新组装。

      • 原位(M=1):part必须为零。两个向量具有相同的VLENGTH。结果始终位于零的原始车道

      此方法是更一般但不太常用的改变形状方法convertShape()的受限版本。此方法的结果与表达式this.convertShape(conv, rsp, this.broadcast(part))相同,其中输出种类为rsp=this.species().withLanes(FTYPE.class)

      类型参数:
      F - 物种的装箱元素类型
      参数:
      conv - 要应用车道方式的所需标量转换
      part - 结果的部分编号,如果既不扩展也不收缩,则为零
      返回:
      通过形状和元素类型从此向量转换的向量
      抛出:
      ArrayIndexOutOfBoundsException - 除非part为零,否则扩展比率为Mpart为正且小于M,或者收缩比率为Mpart为负且大于-M
      参见:
    • convertShape

      public abstract <F> Vector<F> convertShape(VectorOperators.Conversion<E,F> conv, VectorSpecies<F> rsp, int part)
      将此向量转换为具有给定物种、形状和元素类型的向量,根据指定的转换将当前ETYPE的车道值转换为新的车道类型(此处称为FTYPE)。这是一个逐车道操作,将输入向量中的ETYPE值复制到结果中对应的FTYPE值。

      如果旧物种和新物种具有相同的形状,则行为与更简单的、形状不变的方法convert()完全相同。在这种情况下,应该使用更简单的方法convert(),以使代码更易于理解。否则,这是一个改变形状的操作,可能具有特殊的实现成本。

      由于形状变化和车道大小变化的综合效果,输入和输出物种可能具有不同的车道计数,导致扩展或收缩。在这种情况下,非零的part参数选择从扩展的逻辑结果中选择部分结果,或将收缩的逻辑结果引导到所需输出物种的物理输出向量中。

      以下伪代码说明了此方法对原位、扩展和收缩转换的行为。(此伪代码也适用于形状不变的方法,但对输出物种有形状限制。)请注意,对于任何特定的转换操作符和形状组合,只有三个代码路径中的一个与之相关。

      
       FTYPE scalar_conversion_op(ETYPE s);
       EVector a = ...;
       VectorSpecies<F> rsp = ...;
       int part = ...;
       VectorSpecies<E> dsp = a.species();
       int domlen = dsp.length();
       int ranlen = rsp.length();
       FTYPE[] logical = new FTYPE[domlen];
       for (int i = 0; i < domlen; i++) {
         logical[i] = scalar_conversion_op(a.lane(i));
       }
       FTYPE[] physical;
       if (domlen == ranlen) { // 原位
           assert part == 0; //否则AIOOBE
           physical = logical;
       } else if (domlen > ranlen) { // 扩展
           int M = domlen / ranlen;
           assert 0 <= part && part < M; //否则AIOOBE
           int origin = part * ranlen;
           physical = Arrays.copyOfRange(logical, origin, origin + ranlen);
       } else { // (domlen < ranlen) // 收缩
           int M = ranlen / domlen;
           assert 0 >= part && part > -M; //否则AIOOBE
           int origin = -part * domlen;
           System.arraycopy(logical, 0, physical, origin, domlen);
       }
       return FVector.fromArray(ran, physical, 0);
       
      类型参数:
      F - 输出物种的装箱元素类型
      参数:
      conv - 要应用车道方式的所需标量转换
      rsp - 所需的输出物种
      part - 结果的部分编号,如果既不扩展也不收缩,则为零
      返回:
      通过元素类型从此向量转换的向量
      参见:
    • castShape

      public abstract <F> Vector<F> castShape(VectorSpecies<F> rsp, int part)
      用于将一个向量从一种车道类型转换为另一种类型的便捷方法,在车道大小发生变化时进行必要的重塑。此方法返回此表达式的值:convertShape(conv,rsp,part),其中convVectorOperators.Conversion.ofCast(E.class,F.class)

      如果旧物种和新物种具有不同的形状,则这是一个形状更改操作,可能具有特殊的实现成本。

      类型参数:
      F - 输出物种的装箱元素类型
      参数:
      rsp - 所需的输出物种
      part - 结果的部分编号,如果既不扩展也不收缩,则为零
      返回:
      通过元素类型从此向量转换的向量
      参见:
    • check

      public abstract <F> Vector<F> check(Class<F> elementType)
      检查此向量是否具有给定的元素类型,并返回此向量不变。效果类似于此伪代码:elementType == species().elementType() ? this : throw new ClassCastException()
      类型参数:
      F - 所需车道类型的装箱元素类型
      参数:
      elementType - 所需的车道类型
      返回:
      相同的向量
      抛出:
      ClassCastException - 如果向量具有错误的元素类型
      参见:
    • check

      public abstract <F> Vector<F> check(VectorSpecies<F> species)
      检查此向量是否具有给定的物种,并返回此向量不变。效果类似于此伪代码:species == species() ? this : throw new ClassCastException()
      类型参数:
      F - 所需物种的装箱元素类型
      参数:
      species - 所需的物种
      返回:
      相同的向量
      抛出:
      ClassCastException - 如果向量具有错误的物种
      参见:
    • intoMemorySegment

      public abstract void intoMemorySegment(MemorySegmentPREVIEW ms, long offset, ByteOrder bo)
      将此向量存储到从偏移开始使用显式字节顺序的内存段预览中。

      根据指定的字节顺序从原始车道元素中提取字节。根据它们的内存顺序,将车道存储。

      此方法的行为就好像调用intoMemorySegment()预览如下:

      
       var m = maskAll(true);
       intoMemorySegment(ms, offset, bo, m);
       
      参数:
      ms - 内存段
      offset - 进入内存段的偏移量
      bo - 预期的字节顺序
      抛出:
      IndexOutOfBoundsException - 如果offset+N*ESIZE < 0offset+(N+1)*ESIZE > ms.byteSize()对于向量中的任何车道N
      UnsupportedOperationException - 如果内存段是只读的
      IllegalArgumentException - 如果内存段是不由byte[]数组支持的堆段。
      IllegalStateException - 如果内存段的会话未激活,或者从拥有会话的线程以外的线程访问。
      自:
      19
    • intoMemorySegment

      public abstract void intoMemorySegment(MemorySegmentPREVIEW ms, long offset, ByteOrder bo, VectorMask<E> m)
      将此向量存储到从偏移开始使用显式字节顺序和掩码的内存段预览中。

      根据指定的字节顺序从原始车道元素中提取字节。根据它们的内存顺序,将车道存储。

      以下伪代码说明了行为,其中JAVA_E是原始元素类型的布局,ETYPE是原始元素类型,EVector是此向量的原始向量类型:

      
       ETYPE[] a = this.toArray();
       var slice = ms.asSlice(offset)
       for (int n = 0; n < a.length; n++) {
           if (m.laneIsSet(n)) {
               slice.setAtIndex(ValueLayout.JAVA_E.withBitAlignment(8), n);
           }
       }
       
      实现注意:
      如果指定的字节顺序与平台本机顺序相同,则此操作可能更有效,因为此方法不需要重新排序车道值的字节。在ETYPEbyte的特殊情况下,忽略字节顺序参数。
      参数:
      ms - 内存段
      offset - 进入内存段的偏移量
      bo - 预期的字节顺序
      m - 控制车道选择的掩码
      抛出:
      IndexOutOfBoundsException - 如果offset+N*ESIZE < 0offset+(N+1)*ESIZE > ms.byteSize()对于向量中的任何已设置掩码的车道N
      UnsupportedOperationException - 如果内存段是只读的
      IllegalArgumentException - 如果内存段是不由byte[]数组支持的堆段。
      IllegalStateException - 如果内存段的会话未激活,或者从拥有会话的线程以外的线程访问。
      自:
      19
    • toArray

      public abstract Object toArray()
      返回一个包含所有车道值的打包数组。数组长度与向量长度相同。数组的元素类型与向量的元素类型相同。数组元素按车道顺序存储。在Vector的子类型上覆盖此方法,指定元素类型的准确类型化数组结果。
      API 注意:
      通常,如果您正在使用具有已知元素类型的向量子类型,则更喜欢强类型访问
      返回:
      包含此向量车道值的准确类型化数组
      参见:
    • toIntArray

      public abstract int[] toIntArray()
      返回一个包含所有车道值的int[]数组,转换为类型int。数组长度与向量长度相同。数组元素按车道顺序转换并存储。如果向量元素类型为floatdouble,当车道包含分数或超出范围的值时,此操作可能失败。如果任何向量车道值无法表示为int,则会抛出异常。
      API 注意:
      通常,如果您正在使用具有已知元素类型的向量子类型,则更喜欢强类型访问
      返回:
      包含此向量车道值的int[]数组
      抛出:
      UnsupportedOperationException - 如果任何车道值无法表示为int数组元素
      参见:
    • toLongArray

      public abstract long[] toLongArray()
      返回一个long[]数组,其中包含所有的车道值,转换为long类型。数组长度与向量长度相同。数组元素按车道顺序转换,如同通过强制转换并按车道顺序存储。如果向量元素类型为floatdouble,当车道包含小数或超出范围的值时,此操作可能失败。如果任何向量车道值无法表示为long,则会抛出异常。
      API注释:
      通常,如果您正在使用具有已知元素类型的向量子类型,则更倾向于强类型访问
      返回:
      包含此向量车道值的long[]数组
      抛出:
      UnsupportedOperationException - 如果任何车道值无法表示为long数组元素
      另请参阅:
    • toDoubleArray

      public abstract double[] toDoubleArray()
      返回一个double[]数组,其中包含所有的车道值,转换为double类型。数组长度与向量长度相同。数组元素按车道顺序转换,如同通过强制转换并按车道顺序存储。如果向量元素类型为long,此操作可能会丢失精度。
      API注释:
      通常,如果您正在使用具有已知元素类型的向量子类型,则更倾向于强类型访问
      返回:
      包含此向量车道值的double[]数组,可能四舍五入以表示可表示的double
      另请参阅:
    • toString

      public abstract String toString()
      返回此向量的字符串表示形式,形式为"[0,1,2...]",报告此向量的车道值,按车道顺序。该字符串产生方式如同调用Arrays.toString()一样,适用于由this.toArray()返回的数组。
      覆盖:
      toString 在类 Object
      返回:
      形式为"[0,1,2...]"的字符串,报告此向量的车道值
    • equals

      public abstract boolean equals(Object obj)
      指示此向量是否与其他对象相同。仅当两个向量具有相同的种类和相同的车道值,且顺序相同时,它们才是相同的。

      车道值的比较方式如同调用Arrays.equals()一样,适用于两个向量上的toArray()返回的数组。

      覆盖:
      equals 在类 Object
      参数:
      obj - 用于比较的参考对象。
      返回:
      此向量是否与其他对象相同
      另请参阅:
    • hashCode

      public abstract int hashCode()
      返回此向量的哈希码值,基于车道值和向量种类。
      覆盖:
      hashCode 在类 Object
      返回:
      此向量的哈希码值
      另请参阅:
    • getPayload

      protected final Object getPayload()