The Vector lanes
A Vector<E> is like a fixed-sized Java array made of lanes. The lane count is returned by the length() method and is called VLENGTH. The lane count is equal to the number of scalar elements stored in that vector.If you know the element size and the shape of the vector then we can compute the number of lanes as (shape/element size). You should get the same result as returned by length(). The element size is returned by elementSize(), and the shape is returned by vectorBitSize() or vectorShape().vectorBitSize().For instance, a vector whose shape is 256 bits with an element type of float (which is 32 bits (4 bytes) in Java) holds 8 float scalar elements, so it has 8 lanes. The following figure depicts this statement:
Figure 5.4 – Computing the number of lanes
Based on this example, you can easily compute the number of lanes for any other vector configuration. Next, let’s see why is important to know about lanes.
The Vector operations
Applying operations on vectors is the climax of our efforts. The number of lanes estimates the SIMD performance because vector operations operate on lanes. A single vector operation affects a lane as a unit of work. For instance, if our vector has 8 lanes, it means that SIMD will perform 8 leanwise operations at once. In the figure below, you can see a comparison of SISD vs. SIMD in this context:
Figure 5.5 – SISD vs. SIMD
While SISD has a single scalar as a unit of work, SIMD has 8 scalars (8 lanes) which explains why SIMD has a significant performance over SISD.So, a Vector<E> is operated on lanes. Mainly, we have leanwise operations (such as addition, division, bit shifts, and so on) and cross-lane operations that reduce all lanes to a single scalar (for instance, summing all lanes). The following figure depicts these statements:
Figure 5.6 – Leanwise and cross-lane operations
Moreover, a Vector<E> can be operated on with a VectorMask<E>. This is a sequence of boolean values that can be used by some vector operations to filter the selection and operation of lane elements of the given input vectors. Check out the following figure (the addition operation is applied only when the mask contains 1):
Figure 5.7 – Leanwise addition with mask
Speaking about vector operations, you should definitely take a look at the Vector and VectorOperators documentation. In the Vector class, we have methods that apply operations between two vectors. For instance, we have methods for binary operations (such as add(), div(), sub(), and mul()), for comparisons (such as eq(), lt(), compare()), for mathematical operations (such as abs()), and so on. Moreover, in VectorOperators we have a bunch of nested classes (for instance, VectorOperators.Associative) and several constants representing leanwise operations such as trigonometric functions (SIN, COS, and so on), bitwise shifting operations (LSHL, LSHR), mathematical operations (ABS, SQRT, POW), and so on.In the following problems, you’ll see at work a part of these operations, but for now, let’s touch on the last and essential topic, creating vectors.
Creating vectors
We already know that having a VectorSpecies is like having a factory for creating vectors of the required element type and shape. Now, let’s see how we can use such a factory to effectively create vectors (fill them up with scalars) that get involved in solving real problems.Let’s assume the following species (a vector of 8 lanes, 32*8=256):
static final VectorSpecies<Integer> VS256
= IntVector.SPECIES_256;
Next, let’s create the most common types of vectors.
Creating vectors of zeros
Let’s assume that we need a vector containing only zeros. A quick approach relies on zero() method as follows:
[0, 0, 0, 0, 0, 0, 0 0]
Vector<Integer> v = VS256.zero();
This produced a vector with 8 lanes of 0.0. The same thing can be obtained from the specialized IntVector class as well via zero(VectorSpecies<Integer> species):
IntVector v = IntVector.zero(VS256);
You can easily extrapolate this example to FloatVector, DoubleVector, and so on.