VMIPS and the art of counting convoys: chaining, lanes, lengths, masks and banks

After this lesson you will pack any vector program of the papers into convoys and give its clock cycles with the table or the timeline the paper asks for — and explain what lanes, the vector-length register, strip mining, the mask register and memory banks change, and what each of them costs.

Question 6 of the quiz block of the 22 June 2026 paper describes a vector processor with registers of 16 elements and three vector units — one load/store unit, one add/subtract unit, one multiply/divide unit, each "with operation chaining" — and gives it seven lines of code: two vector loads, three arithmetic instructions, two vector stores. "How many convoys? How many clock cycles to execute the code?" Answer 1: 2 convoys, 32 cycles. Answer 2: 3 convoys, 48. Answer 3: 4 convoys, 64. Answer 4: 5 convoys, 80.

The July 2024 paper asked the same question on the same code, and every paper since has one like it. Three of the four answers are wrong, and each is what one particular slip produces. The unit that decides the right one does no arithmetic at all — and the same unit, with the memory behind it, decides how fast a vector machine can walk down a column of a matrix.

Convoys and chimes: time counted in whole vectors

Lesson 30 built VMIPS: the scalar MIPS pipeline plus a vector unit — vector registers of 64 elements of 64 bits, deeply pipelined vector functional units, one vector load/store unit — so that DAXPY, \(Y = a \cdot X + Y\) on 64 elements, takes 6 instructions instead of about 578 (p. 402). Here you count its clock cycles. These are its five vector instructions as the papers write them; F0 already holds the scalar a.

L.V      V1, RX       # V1 ← X
MULVS.D  V2, V1, F0   # V2 ← a * X
L.V      V3, RY       # V3 ← Y
ADDVV.D  V4, V2, V3   # V4 ← a * X + Y
S.V      V4, RY       # Y  ← V4

A vector unit is a deep pipeline that accepts one new element per clock cycle. MULVS.D on 64 elements keeps the multiplier busy for 64 cycles, plus a few cycles while the first element travels down the pipeline — a start-up that the papers' counts leave out. Picture each unit as a car-wash tunnel: a vector instruction sends a train of 64 cars through it, one car per cycle. The whole question is which trains may roll at the same time.

Convoy — a set of vector instructions that execute together, each on its own functional unit. Two rules: at most one instruction per unit in a convoy (and the load/store unit is a unit), and an instruction that needs a vector produced inside the same convoy may join it only if the machine has chaining.

Chime — the time one convoy takes: VL clock cycles when every unit handles one element per cycle. A sequence packed into m convoys runs in m chimes.

\[ T \approx m \times \mathrm{VL} \quad \text{clock cycles} \]
\(m\)
number of convoys of the sequence
\(\mathrm{VL}\)
vector length: elements per vector register — 64 on VMIPS, 16 or 8 on the papers' machines
\(T\)
execution time with one element per cycle per unit (one lane), start-up ignored

Now the dependences. MULVS.D needs V1, which L.V is still filling. Without chaining, the multiply waits until the load has written the whole of V1 — the last of its 64 elements — so the two cannot share a convoy. With chaining, the first element of V1 goes from the load unit straight into the multiplier, the way forwarding passes a result to the next instruction in the five-stage pipeline. The multiply trails the load by a few cycles, element by element, inside the same chime: chaining is forwarding for vectors.

Chaining was not invented for textbooks. Describing the Cray-1 (1976), Richard Russell of Cray Research wrote in 1978 that chaining lets the machine "generate interim results and use them again immediately without additional memory references" — and that its memory "is organized in 16 banks", a detail that comes back in the last section.

0 64 128 192 256 Load/Store Mul/Div Add/Sub L.V V1 L.V V3 S.V V4 MULVS ADDVV

Before: no chaining — 4 chimes, 256 cycles

0 64 128 192 256 Load/Store Mul/Div Add/Sub L.V V1 L.V V3 S.V V4 MULVS ADDVV saved

After: chaining — 3 chimes, 192 cycles

Fig. 1. DAXPY on VMIPS: VL = 64, one load/store unit, one multiplier, one adder; one row per unit, one column per chime. Orange arrows: an instruction waiting for the whole vector of its producer. Magenta arrows: chaining — the first element is forwarded, and the consumer trails its producer by a few cycles. With chaining the load/store row works in every chime; without it, the third chime belongs to the adder alone.

Without chaining every true dependence ends a convoy: the multiply comes after the first load, the add after the multiply, the store after the add — four convoys, 4 × 64 = 256 cycles. With chaining the multiply joins the first load and the add joins the second load: three convoys, 3 × 64 = 192 cycles. The store would chain on the add too, but the load/store unit is busy with L.V V3 in that convoy. The paper asks for the same result as a table, one row per convoy and one column per unit:

DAXPY on VMIPS with chaining: 3 convoys × 64 = 192 clock cycles.
Load/Store Vector UnitMul/Div Vector UnitAdd/Sub Vector Unit
1^ convoyL.V V1, RXMULVS.D V2, V1, F0chained
2^ convoyL.V V3, RYADDVV.D V4, V2, V3chained
3^ convoyS.V V4, RY
DAXPY on VMIPS without chaining: 4 convoys × 64 = 256 clock cycles.
Load/Store Vector UnitMul/Div Vector UnitAdd/Sub Vector Unit
1^ convoyL.V V1, RX
2^ convoyL.V V3, RYMULVS.D V2, V1, F0
3^ convoyADDVV.D V4, V2, V3
4^ convoyS.V V4, RY

With chaining, only the structural rule — one instruction per unit — separates convoys; without it, every true dependence does too.

The quiz of every paper, in four moves

The three official papers (July 2024, June 2025, June 2026) each have a convoy quiz worth 1 point, plus 1 for the table or timeline that motivates it, on a machine described in the same words: a vector register file of "32 vectors of 16 elements" (or of 8) "per 64 bits/element", "One Load/Store Vector Unit with operation chaining and memory bandwidth 64 bits", one ADD/SUB and one MUL/DIV unit, both with chaining. The February 2025 and January 2026 papers transcribed in the notes have a quiz of the same kind. Four moves solve it.

  1. Read the chime. VL is the number of elements per register, 16 or 8. A memory bandwidth of 64 bits moves one 64-bit element per cycle, and so does every arithmetic unit: a convoy lasts VL cycles.
  2. Find the floor. Count the instructions of each unit. The busiest unit needs at least that many convoys. On every paper's machine it is the single load/store unit, so the number of vector loads and stores is a lower bound on m.
  3. Pack. Put each instruction into the earliest convoy where its unit is still free and each source vector is either complete — written in an earlier convoy — or produced in the same convoy by an instruction it chains to. An instruction does not wait for unrelated instructions above it in the listing.
  4. Multiply and draw. Cycles = m × VL. Then fill the paper's table (one row per convoy, one column per unit) or its timeline (one strip of VL cycles per convoy).

Move 3 is the counting of the notes' solutions and of every table in this lesson: an independent instruction may run ahead of unrelated ones above it. In the June 2025 quiz the first multiply joins the first load, although two more loads stand between them in the listing; move 2 then explains why that quiz cannot go below four convoys.

Know the other reading too. A vector unit that issues strictly in listing order cannot start that multiply before the third load, and packing that way gives the June 2025 code 6 convoys, 48 cycles; on the other papers' code both readings give the same count. No official key settles it, so write the assumption in one line under your table. Try the moves yourself before reading the solutions below.

Convoy builder: the exam table with the rules built in

The paper's table: one row per convoy, one column per unit, with the headings of the June 2025 paper; the first load is already in the first row, as in that paper. Click an instruction above the table, then a cell in the column of its unit; click a filled cell to take its instruction back. A cell holds one instruction — that is the structural rule. A cell turns red when it breaks a dependence rule, and the note says which. Under the table, the timeline: one strip of VL cycles per convoy, one row per unit. The insight to take away: once chaining is on, the busiest unit — the single load/store unit — fixes the number of convoys.

  1. Program "July 2024 = June 2026", chaining on. Before you place anything: below how many convoys can the answer not go, and why? Then build the table yourself and compare it with "Pack the convoys".
    What you should see

    Four: four memory instructions share one load/store unit. Pack puts ADDVS.D V2 and MULVS.D V2 with L.V V1 (both chained), ADDVS.D V3 with L.V V3, then one store per convoy: 4 × 16 = 64 cycles. In the timeline the Load/Store row is full, while the Mul/Div unit works in one convoy of four.

  2. Keep that table and switch chaining off. Which cells turn red, and what does Pack give now?
    What you should see

    Three cells: ADDVS.D V2 (V1 comes from the load in its convoy), MULVS.D V2 (V2 from the add in its convoy) and ADDVS.D V3 (V3 from the second load). Pack: 5 convoys, 80 cycles — Answer 4 of the June 2026 paper, the count you get by forgetting chaining.

  3. Program "June 2025", chaining on. Predict the floor, then pack. Which instruction ends up in the first convoy although two loads stand between it and its own load in the listing? Then clear the table and fill it strictly in listing order.
    What you should see

    Floor 4 (three loads and a store), reached: 4 × 8 = 32 cycles. MULVS.D V1, V1, F1 joins L.V V1, RA: it depends on neither L.V V2 nor L.V V3. Each add chains on the multiply of its convoy and finds its other operand complete. In listing order the multiplies queue behind the loads: L.V V1; L.V V2; L.V V3 + MULVS.D V1; MULVS.D V2; MULVS.D V3 + ADDVV.D V2; ADDVV.D V3 + S.V V3 — 6 convoys, which the note calls valid but not minimal. With chaining off, Pack also needs 6 convoys, 48 cycles: Answer 5.

  4. Program "February 2025", chaining on: pack it, then rebuild the notes' table of p. 581, which puts S.V V1, RX in the second convoy and L.V V3, RB in the third. Is it legal?
    What you should see

    Yes. V1 is complete after the first convoy, and the store and the second load touch different registers and different addresses (RX, RB). Same count either way: 5 convoys, 40 cycles.

Now the longest quiz, from the February 2025 paper (transcribed in the notes, p. 580): nine instructions on registers of 8 elements, same machine.

L.V      V1, RA
L.V      V3, RB
MULVS.D  V1, V1, F0
ADDVV.D  V2, V1, V1
MULVS.D  V2, V2, F0
ADDVV.D  V3, V2, V3
S.V      V1, RX
S.V      V2, RY
S.V      V3, RZ

Move 2 gives the answer before any table: two loads and three stores on one load/store unit need at least 5 convoys. Move 3 shows that five are enough, so the answer is 5 convoys; 5 × 8 = 40 clock cycles — Answer 4, as the notes say.

February 2025, quiz 1, with chaining: 5 convoys × 8 = 40 clock cycles (recomputed).
Load/Store Vector UnitMul/Div Vector UnitAdd/Sub Vector Unit
1^ convoyL.V V1, RAMULVS.D V1, V1, F0chainedADDVV.D V2, V1, V1chained
2^ convoyL.V V3, RBMULVS.D V2, V2, F0ADDVV.D V3, V2, V3chained
3^ convoyS.V V1, RX
4^ convoyS.V V2, RY
5^ convoyS.V V3, RZ
The same code without chaining: 6 convoys × 8 = 48 clock cycles (Answer 5, the distractor).
Load/Store Vector UnitMul/Div Vector UnitAdd/Sub Vector Unit
1^ convoyL.V V1, RA
2^ convoyL.V V3, RBMULVS.D V1, V1, F0
3^ convoyS.V V1, RXADDVV.D V2, V1, V1
4^ convoyMULVS.D V2, V2, F0
5^ convoyS.V V2, RYADDVV.D V3, V2, V3
6^ convoyS.V V3, RZ

In the first convoy the load feeds the multiply, which feeds the add: a chain of three. In the second, MULVS.D V2 reads the V2 that the first convoy completed, and ADDVV.D V3 chains on two producers at once, the multiply and the load of V3. The notes' own table puts S.V V1 before the second load — also legal, since V1 is complete after the first convoy — with the same count. Without chaining every true dependence costs a convoy, and the chain L.V V1 → MULVS.D V1 → ADDVV.D V2 → MULVS.D V2 → ADDVV.D V3 → S.V V3 has six links: 6 convoys, 48 cycles.

Why 8 cycles per convoy? The notes never say. Each register holds 8 elements of 64 bits; the load/store unit moves 64 bits — one element — per cycle, and so does every arithmetic unit; the start-up of the pipelines is not counted. A convoy is therefore a chime of 8 cycles, and every option of the quiz is a multiple of 8: 16, 24, 32, 40, 48.

"Chaining puts dependent instructions together, so with chaining everything fits in one or two convoys." No: chaining removes the data constraint only. The structural one — one instruction per unit per convoy — stays, and on the papers' machines it is the one that sets the count: the load/store unit works in every convoy of every chained solution above.

Lanes: several elements per cycle, the same instructions

So far a unit handled one element per cycle, and a chime lasted VL cycles. Hardware can do better without touching the program: it replicates each pipeline into L lanes. Take ADDVV.D V3, V1, V2 on 16 elements. With one lane, element 0 enters the adder in the first cycle and element 15 in the sixteenth. With four lanes, four adders take elements 0 to 3 in the first cycle and 4 to 7 in the second: the vector is through in 4 cycles.

cycle 1 4 8 12 16 lane 0 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 cycles

Before: one lane — one element per cycle

cycle 1 4 8 12 16 lane 0 lane 1 lane 2 lane 3 0 4 8 12 1 5 9 13 2 6 10 14 3 7 11 15 4 cycles lane is i mod 4

After: four lanes — four elements per cycle

Fig. 2. The same ADDVV.D on 16 elements; the numbers in the cells are element indices. Before: one lane, 16 cycles. After: element i goes to lane i mod 4 — lane 1 handles elements 1, 5, 9 and 13 — and the vector takes ⌈16/4⌉ = 4 cycles. The instruction did not change.

The interleaving of Fig. 2 holds for every register: element i lives in lane i mod L, so an element-wise operation never needs data from another lane, and the lanes need no wires between them. Each lane is a slice of the register file plus one pipeline of each unit (p. 405; the notes' Figure 30, p. 392, draws elements 0, 4, … 60 in the first of four lanes). With L lanes a vector of n elements takes ⌈n / L⌉ cycles (p. 405), and the chime model becomes:

\[ T \approx m \times \left\lceil \frac{\mathrm{VL}}{L} \right\rceil \]
\(L\)
number of lanes; element i of every register lives in lane \(i \bmod L\)
\(m,\ \mathrm{VL}\)
convoys and vector length, as before

The notes' Example 3 is DAXPY with VL = 64: the multiply takes 64 cycles on one lane and 16 on four, the add likewise (p. 405). Chained, the three convoys take 3 × 16 = 48 cycles instead of 192.

The instruction set does not change: the same ADDVV.D runs on one lane or on eight, and the same binary gets faster — the notes say vector machines scale "transparently" with hardware width, unlike scalar unrolling (p. 406), which rewrites the code for every width. The cost is paid elsewhere: the register file needs read and write ports for every lane, and the load/store unit must deliver L words per cycle. Where those words come from is the subject of the last section.

Lanes divide the chime, not the number of convoys: T ≈ m × ⌈VL/L⌉.

Longer than a register: MVL, VLR and strip mining

A loop rarely has exactly 64 iterations. Take n = 100 on VMIPS, whose registers hold 64 elements. The first pass handles elements 0 to 63. If every vector instruction always processed a whole register, the second pass would touch elements 64 to 127: 28 elements past the end of the arrays, read and written by mistake (pp. 407–408). One register fixes it.

Maximum vector length (MVL) — the number of elements a vector register can hold: a hardware constant, 64 on VMIPS, 32 on the machine of the June 2026 quiz.

Vector-length register (VLR) — a control register that tells every vector instruction how many elements to process: \(0 \le \mathrm{VLR} \le \mathrm{MVL}\) (eq. 83).

The second pass therefore sets VLR = 36 and processes elements 64 to 99; the other 28 entries of the register are simply not used. MVL is the hardware; VLR is the knob the software turns.

When n is larger than MVL the compiler applies strip mining: it cuts the loop into strips of at most MVL elements — ⌊n / MVL⌋ full strips and, when n mod MVL is not zero, one short strip with VLR = n mod MVL (eq. 84, p. 408). The notes' example: 1000 = 15 × 64 + 40.

\[ \text{strips} = \left\lceil \frac{n}{\mathrm{MVL}} \right\rceil \] \[ \text{short strip: } \mathrm{VLR} = n \bmod \mathrm{MVL} \]
\(n\)
trip count of the loop
\(\mathrm{MVL}\)
maximum vector length of the machine

Two papers ask it as a quiz: for (i=0; i<260; i++) Y[i] = X[i] + Y[i]; on VMIPS with "8 registers of 64 elements" (July 2024) or "16 Vector registers of 32 elements" (June 2026) — which transformation: trace scheduling, software pipelining, strip mining or a mask register? Strip mining: 260 = 4 × 64 + 4, or 260 = 8 × 32 + 4. Trace scheduling and software pipelining are compiler techniques that reorder scalar instructions for a VLIW (lesson 21 meets this very quiz from their side), and a mask register is for an if inside the loop, which this loop does not have. The number of registers is not the problem either: the loop needs two.

Hennessy and Patterson write the strip-mined loop with the short strip first; the notes put it last. Both are correct:

low = 0;
VL = n % MVL;                      /* odd-size strip first */
for (j = 0; j <= n / MVL; j++) {   /* one pass per strip   */
    for (i = low; i < low + VL; i++)
        Y[i] = a * X[i] + Y[i];    /* vector code, VLR = VL */
    low = low + VL;
    VL = MVL;                      /* later strips are full */
}

The inner loop becomes one sequence of vector instructions, executed once per strip with VLR set to VL. The price is the scalar strip loop around them, a few instructions per strip that the papers' counts ignore.

Strip-mining and VLR slicer

The bar is the loop: one block per strip, at the scale of one full register per strip, with the short strip last as in the notes (marked with the highlighter). Under it, one vector register during the last strip: blue entries are processed, grey ones are left alone. Move n and MVL, and switch the length register off to see what it prevents. The insight to take away: MVL is the hardware — the register always has MVL entries — while VLR is the knob the program turns on every strip.

  1. Set n = 260 with MVL = 64, the July 2024 quiz. Predict the number of strips and the VLR of the short one. Then switch to MVL = 32, the June 2026 quiz.
    What you should see

    5 strips: 4 × 64 + 4. With MVL = 32: 9 strips, 8 × 32 + 4. The short strip has 4 elements both times, because 260 mod 64 = 260 mod 32 = 4.

  2. Switch the vector-length register off. Predict how many elements the last strip touches past the end of the arrays, with MVL = 64 and with MVL = 32.
    What you should see

    60 and 28: 320 − 260 and 288 − 260. That is the wrong behaviour described on p. 408: without VLR every instruction processes the whole register.

  3. VLR back on, MVL = 64. Try n = 31, then 64, then 65. Which of them needs strip mining at all?
    What you should see

    31: one strip with VLR = 31 — no strip mining, only the length register. 64: one full strip. 65: two strips, the second with VLR = 1 — a whole extra strip, with its loop overhead, for one element.

An if inside the loop: the vector mask register

Now a loop that is parallel but not uniform (p. 409):

for (i = 0; i < 64; i++)
    if (X[i] != 0)
        X[i] = X[i] - Y[i];

Every iteration is independent, yet a single SUBVV.D would subtract at every element, including those the if skips. A vector processor has one program counter for all 64 elements: there is nothing to branch around element by element. Instead it turns the condition into data.

Vector mask register (VM in Hennessy & Patterson, VMR in the notes) — a register of MVL bits, one per element. A vector compare sets bit i when the condition holds for element i; while masking is on, every vector instruction writes only the elements whose bit is 1 and leaves the others unchanged — predication at the level of vectors.

In Hennessy and Patterson's VMIPS, written with the papers' load and store, the loop becomes (F0 holds 0.0):

L.V      V1, RX       # V1 ← X
L.V      V2, RY       # V2 ← Y
SNEVS.D  V1, F0       # VM(i) = 1 if V1(i) != F0
SUBVV.D  V1, V1, V2   # subtract under the mask
S.V      V1, RX       # X ← V1
every element costs a cycle X VM X − Y 3 0 7 2 0 0 5 1 0 4 6 0 2 0 8 9 1 0 1 1 0 0 1 1 0 1 1 0 1 0 1 1 v[0] v[15]
Fig. 3. Sixteen elements under a mask. The compare sets VM where X is not zero (violet: the mask is bookkeeping); SUBVV.D then updates the 10 elements with VM = 1 (blue) and leaves the 6 crossed ones untouched. The subtracter still spends a cycle on each of the 16.

The mask keeps the loop vectorizable, with no scalar fallback, and works with any vector instruction (p. 409). What it does not do is save time: SUBVV.D still streams all 64 elements through the subtracter, masked or not, so it takes the same 64 cycles. In Fig. 3, 6 of the 16 cycles do no useful work; the more elements are masked off, the lower the efficiency — still better than running the loop as scalar code (p. 410). Lesson 32 meets the same price under another name, branch divergence on a GPU.

A mask preserves vectorizability, not speed: a masked element still costs its cycle.

Memory banks: when the stride keeps hitting the same bank

A load/store unit that delivers one 64-bit word per cycle needs a memory that can keep up, and one memory bank cannot: after an access it stays busy for several cycles. Vector machines therefore spread consecutive words over many memory banks, round-robin (p. 411):

\[ \text{bank} = \frac{\text{address}}{\text{word size}} \bmod \#\text{banks} \]

With 8 banks, words 0 to 7 sit in banks 0 to 7 and word 8 in bank 0 again. Walk a row of a C matrix — consecutive words, stride 1 — and every bank serves one element and is not asked again for eight cycles. Now walk a column of a matrix whose rows hold 128 words: consecutive elements are 128 words apart, and 128 mod 8 = 0, so every element lands in the same bank (p. 412). The notes' picture is a supermarket with eight checkouts and eight shoppers all queuing at checkout 0, while seven cashiers wait.

time B0 B1 B2 B3 B4 B5 B6 B7 0 128 256 384 512 640 768 896 7 banks idle

Before: rows of 128 words — stride 128

time B0 B1 B2 B3 B4 B5 B6 B7 0 129 258 387 516 645 774 903

After: rows padded to 129 words — stride 129

Fig. 4. The first eight elements of a column walk on 8 banks, time running downward; each cell holds the word address of an element. Before: 128k mod 8 = 0, every element in bank 0, each later one waiting (orange, dashed) until the bank is free again. After: 129k mod 8 = k, so element k goes to bank k and no element waits.

The number of different banks a strided access visits is (p. 412):

\[ \frac{\#\text{banks}}{\gcd(\text{stride},\ \#\text{banks})} \]
stride
distance in words between consecutive elements: 1 along a row of a C matrix, the row length down a column (the notes' LVWS V1, (R1, R2) loads V1 from base R1 with stride R2)
gcd
greatest common divisor

Stride 1 and every odd stride visit all 8 banks; stride 2 visits 4; stride 4, two; strides 8, 32 and 128 visit one. Whether that costs time depends on how long a bank stays busy. Hennessy and Patterson's example, not in the notes: 8 banks, a bank busy time of 6 cycles, a memory latency of 12, a 64-element load. With stride 1 each bank is reached again only after 8 elements, when its 6 busy cycles are over: 12 + 64 = 76 cycles. With stride 32 every access waits for the one bank: 12 + 1 + 6 × 63 = 391 cycles, five times slower. The steps below show where the gcd comes from, and when a repeated bank hurts:

  1. Element k of an access with stride s, starting at word address \(a_0\), lies at \(a_0 + k s\); with N banks its bank is

    \[ b_k = (a_0 + k\,s) \bmod N . \]
  2. Elements k and k + d fall in the same bank exactly when the extra distance d·s is a multiple of N:

    \[ d\,s \equiv 0 \pmod{N} . \]
  3. Write \(g = \gcd(s, N)\), so \(s = g s'\) and \(N = g N'\) with s′ and N′ coprime. Dividing by g, d·s′ must be a multiple of N′; since s′ shares no factor with N′, d itself must be one, and the smallest such d is

    \[ d = N' = \frac{N}{\gcd(s, N)} . \]
  4. So the banks repeat with period N / gcd(s, N): the first N / gcd(s, N) elements all fall in different banks, and the access visits exactly that many — formula (86).

  5. At one access per cycle the same bank is hit again every N / gcd(s, N) cycles, but it is free only after its busy time t. The access stalls exactly when the bank comes back too soon:

    \[ \frac{N}{\gcd(s, N)} \lt t . \]
  6. 8 banks and t = 6: stride 1 gives 8 ≥ 6, no stall (76 cycles); stride 4 gives 2 < 6, a wait every second element (200 cycles); stride 32 gives 1 < 6, a wait at every element (391 cycles).

The notes list three fixes (pp. 412–413). Change the traversal order, so that the inner loop walks along rows — the loop interchange of lesson 25, met there for caches. Pad each row with one unused word: the column stride becomes 129, gcd(129, 8) = 1, and the column spreads over all eight banks, as in Fig. 4. Or store the matrix column by column (Fortran, MATLAB) when columns are what the code walks. With a power-of-two number of banks every odd stride is harmless (given at least as many banks as busy cycles), while more banks of the same kind do not help a stride that is a multiple of their number: 16 banks and stride 128 still give one bank.

Sparse data have no stride at all. A gather loads a vector from the addresses given by an index vector and a scatter stores to them (p. 413) — LVI and SVI in Hennessy and Patterson's VMIPS. They keep sparse matrices vectorizable, at a cost: every element is a separate access, free to collide with the others.

Bank-conflict simulator

A 64-element strided load, as in Hennessy and Patterson's model: one access issued per cycle at most, an access waits while its bank is busy, the memory latency is 12 cycles. Each blue bar is one access on the row of its bank, as long as the bank busy time; an orange column is a cycle in which the load/store unit waits for a busy bank. Pick a pattern, then move the sliders. The insight to take away: a column walk with stride 128 on 8 banks hits one bank, and one word of padding fixes it.

  1. Pattern "row walk". Predict the banks visited and the load time. Then pick "column, 128-word rows".
    What you should see

    Row walk: 8 banks, 12 + 64 = 76 cycles — every bank gets 8 cycles between its accesses, more than the 6 it needs. Column: 128 mod 8 = 0, one bank, and every access waits out the busy time: 12 + 379 = 391 cycles, 6.11 per element.

  2. Predict the column walk with rows padded to 129 words, then check. Then go back to stride 128 and raise the number of banks to 16.
    What you should see

    Padded: 129 mod 8 = 1, element k in bank k mod 8, eight banks, 76 cycles again. With 16 banks and stride 128 the load still uses one bank, since gcd(128, 16) = 16: 391 cycles. More banks do not cure a power-of-two stride; one word of padding does.

  3. Banks 8, stride 4, busy time 6: somewhere between 76 and 391? Predict, then lower the busy time to 2.
    What you should see

    Two banks (8 / gcd(4, 8)), fewer than 6 cycles of busy time: 200 cycles, a wait every second element. With a busy time of 2 the same two banks recover in time: 76 cycles. The rule: a stall exactly when the banks visited are fewer than the busy time.

  4. Try the odd strides 3, 7 and 127 on 8 banks. Then set 7 banks with stride 128.
    What you should see

    All 76 cycles: an odd stride has gcd 1 with 8, so all eight banks are used. With 7 banks, 128 is coprime with 7 as well, 7 banks ≥ 6 cycles, 76 cycles — the reason some machines used a prime number of banks.

Back to the quiz of the hook. The unit that decides is the load/store unit. The seven lines contain four memory instructions, and one load/store unit takes one of them per convoy: at least four convoys, so Answers 1 and 2 are impossible on sight. Four are enough — L.V V1 with ADDVS.D V2 and MULVS.D V2 chained behind it, L.V V3 with its add, then one store per convoy. Answer 4, 5 convoys and 80 cycles, is the count without chaining. Answer 3 is right: 4 × 16 = 64 clock cycles. And this section showed why that unit matters twice: its one word per cycle is itself a promise that only the banks can keep. A vector processor is as fast as its memory lets it be.

Exam-style questions

Consider a vector processor architecture, as VMIPS shown in the figure (main memory, vector load/store unit, vector registers, scalar registers, and the FP add/subtract, FP multiply, FP divide, integer and logical vector units).

Present the main concepts. Present the main advantages of vector execution with respect to scalar execution, detailing the features of the vector architecture that provide the advantage.

Show model answer

Main concepts. A vector processor is a SIMD machine: one instruction operates on whole vectors held in vector registers. VMIPS is the scalar MIPS pipeline plus a vector unit: (1) a vector register file — 8 registers of 64 elements of 64 bits, with many read and write ports (16 and 8) through a crossbar — a compiler-controlled buffer between memory and the functional units; (2) deeply pipelined vector functional units (FP add/subtract, FP multiply, FP divide, integer, logical) that deliver one result per cycle after a start-up latency; (3) a vector load/store unit that streams one word per cycle from interleaved memory banks, with unit-stride, strided (LVWS) and indexed (gather/scatter) accesses; (4) scalar registers for addresses, strides and the scalar operands of instructions such as MULVS.D; (5) the vector-length register VLR (0 ≤ VLR ≤ MVL), used with strip mining for loops of any length, and the vector mask register for loops with an IF. Execution time is counted in convoys — instructions that run together, one per unit, dependent ones only with chaining — and chimes of about VL cycles: T ≈ convoys × VL.

Advantages and the feature that provides each. (a) Far fewer instructions fetched, decoded and issued: one vector instruction does the work of a whole loop (DAXPY on 64 elements: 6 instructions instead of about 578) — vector registers and vector instructions; less instruction bandwidth and energy. (b) Almost no loop overhead: index updates and loop branches disappear, one branch per strip of up to MVL elements instead of one per element, hence far fewer control hazards and mispredictions — vector-length semantics, VLR and strip mining. (c) Deep pipelines without stalls: the elements of a vector are independent, so no hazard checks inside an instruction, and dependences between vector instructions are checked once per vector, not once per element — pipelined vector units. (d) Overlap of dependent operations and of several units: chaining forwards element by element and several units run in one convoy (DAXPY in 3 chimes, 192 cycles, instead of 4 chimes, 256) — multiple units plus chaining. (e) Memory latency paid once per vector and a sustained bandwidth of one word per cycle: known access patterns let the load/store unit stream from interleaved banks, with little or no data cache — vector load/store unit plus banks. (f) Scalability without changing the code: L lanes process L elements per cycle, ⌈VL/L⌉ cycles per chime, same instructions. Limits: the gain needs data-level parallelism; masked elements still cost cycles; bad strides cause bank conflicts.

This is Question 2 of the February 2025 paper (notes, p. 578); lesson 30 redraws its VMIPS figure. The question has two halves: answer both. For the concepts, name the parts of the figure and what each does; for the advantages, tie each one to the feature that delivers it — "fewer instructions, because one vector instruction encodes a whole loop" — with a number where you have one (6 against 578 instructions, 192 against 256 cycles). One line on the limits shows that you know where vectors stop helping.

Explain the main concepts and benefits of each one of the following techniques used by vector processors: (1) Chaining; (2) Multiple lanes; (3) Vector length register and strip mining; (4) Vector mask registers; (5) Interleaved memory banks and strided accesses.

Show model answer

(1) Chaining: the result of a vector unit is forwarded element by element to the next unit, so a dependent vector instruction starts as soon as the first element of its operand is produced, instead of waiting until the whole vector is written back. Benefit: dependent instructions run overlapped in the same convoy, one chime instead of two per dependence (DAXPY: 4 convoys, 256 cycles → 3 convoys, 192 cycles). It is the vector analogue of forwarding; it does not remove structural hazards — one instruction per unit per convoy.

(2) Multiple lanes: each vector unit and the vector register file are split into L lanes; element i of every register lives in lane i mod L, so element-wise operations never cross lanes. Benefit: L elements per cycle, a chime of ⌈VL/L⌉ cycles (64 elements on 4 lanes: 16 cycles), with no change to the ISA or the code. Cost: register-file ports and memory bandwidth grow with L.

(3) VLR and strip mining: MVL is the hardware length of a vector register; the vector length register (0 ≤ VLR ≤ MVL) tells each instruction how many elements to process. Strip mining is the compiler transformation that cuts a loop of n iterations into ⌈n/MVL⌉ strips, the short one with VLR = n mod MVL (260 elements on MVL 64: 4 × 64 + 4). Benefit: one vector loop for any n, no access past the end of the arrays. Cost: a scalar strip loop with its overhead.

(4) Vector mask registers: one bit per element, set by a vector compare; instructions executed under the mask update only the elements whose bit is 1. Benefit: loops with an IF stay vectorizable (predication at vector level), no scalar fallback, any instruction can be masked. Cost: masked elements still take their cycle, so efficiency equals the fraction of active elements.

(5) Interleaved banks: consecutive words go to consecutive banks (bank = (address / word size) mod #banks), so a unit-stride access finds a free bank every cycle and the load/store unit sustains one word per cycle; strided loads (LVWS) and gather/scatter through index vectors handle matrix columns and sparse data. A stride visits #banks / gcd(stride, #banks) banks; if that is less than the bank busy time, accesses stall (8 banks, busy time 6, latency 12, 64 elements: stride 128 hits one bank and takes 391 cycles instead of 76). Fixes: pad rows (129 words), interchange the loops, choose a column-major layout.

The June 2026 paper asked eight such sub-points in one 8-point question, about five lines each; the 2024 and 2025 papers had 5-point open questions. A complete sub-point says what the mechanism does, what it buys — with a number — and what it costs.

Let's consider the following code executed by a Vector Processor with: Vector Register File composed of 32 vectors of 16 elements per 64 bits/element; Scalar FP Register File composed of 32 registers of 64 bits; One Load/Store Vector Unit with operation chaining and memory bandwidth 64 bits; One ADD/SUB Vector Unit with operation chaining; One MUL/DIV Vector Unit with operation chaining.

L.V      V1, R1       # Load vector from mem. addr. R1 into V1
L.V      V3, R2       # Load vector from mem. addr. R2 into V3
ADDVS.D  V2, V1, S1   # FP add vector V1 to scalar S1
MULVS.D  V2, V2, S2   # FP multiply vector V2 to scalar S2
ADDVS.D  V3, V3, S3   # FP add vector V3 to scalar S3
S.V      V2, R1       # Store vector V2 into memory addr. R1
S.V      V3, R2       # Store vector V3 into memory addr. R2

How many convoys? How many clock cycles to execute the code? Motivate your answer by completing the timing scheme (0, 1, … 15 for the first convoy).

  • 2 convoys; 32 clock cycles Four memory instructions share one load/store unit: two convoys would need two of them in each.
  • 3 convoys; 48 clock cycles A load phase, an arithmetic phase and a store phase: that puts two loads and two stores into single convoys, and both ADDVS.D on the one adder.
  • 4 convoys; 64 clock cycles The load/store unit forces four convoys and chaining packs the arithmetic into the first two: 4 × 16 = 64.
  • 5 convoys; 80 clock cycles The count without chaining: ADDVS.D V2 can no longer join L.V V1, nor MULVS.D join ADDVS.D.
Show model answer

Answer 3: 4 convoys; 64 clock cycles. Four memory instructions on one load/store unit need at least four convoys, and four are enough. A convoy lasts 16 cycles: 16 elements, one 64-bit element per cycle (bandwidth 64 bits), start-up ignored.

Convoys and timing scheme, one strip of 16 cycles per convoy (chained instructions start a few cycles after their producer).
firstinstructions of the convoylast
01… L.V V1, R1 · ADDVS.D V2, V1, S1 (chained) · MULVS.D V2, V2, S2 (chained) …15
1617… L.V V3, R2 · ADDVS.D V3, V3, S3 (chained) …31
3233… S.V V2, R1 …47
4849… S.V V3, R2 …63

The same question was Question 6 of July 2024, with the options 3/48, 2/32 and 4/64. The motivation point is the scheme: four strips of 16 cycles. Say why the count cannot be lower — four memory instructions, one load/store unit — and why chaining matters: without it, 5 convoys and 80 cycles. Swapping the second and third convoy (S.V V2 before L.V V3) is equally correct, and so is the scheme packed strictly in listing order: L.V V1 alone in the first strip, then L.V V3 + ADDVS.D V2 + MULVS.D V2, then ADDVS.D V3 + S.V V2, then S.V V3 — the same 4 convoys.

Let's consider the following code executed by a Vector Processor with: Vector Register File composed of 32 vectors of 8 elements per 64 bits/element; Scalar FP Register File composed of 32 registers of 64 bits; One Load/Store Vector Unit with operation chaining and memory bandwidth 64 bits; One ADD/SUB Vector Unit with operation chaining; One MUL/DIV Vector Unit with operation chaining.

L.V      V1, RA
L.V      V2, RB
L.V      V3, RC
MULVS.D  V1, V1, F1
MULVS.D  V2, V2, F2
MULVS.D  V3, V3, F3
ADDVV.D  V2, V2, V1
ADDVV.D  V3, V2, V3
S.V      V3, RD

How many convoys? How many clock cycles to execute the code? Motivate your answer by completing the table (Load/Store Vector Unit, Mul/Div Vector Unit, Add/Sub Vector Unit; 1^ convoy: L.V V1, RA).

  • 2 convoys; 16 clock cycles Four memory instructions on one load/store unit need at least four convoys.
  • 3 convoys; 24 clock cycles Three loads and a store cannot fit in three convoys on one load/store unit.
  • 4 convoys; 32 clock cycles Each multiply chains on its load and each add on its multiply; the store needs a fourth convoy: 4 × 8 = 32.
  • 5 convoys; 40 clock cycles No reading of the rules gives five: chaining with the packing of move 3 reaches the floor of four, and both the count without chaining and the strict listing-order packing give six.
  • 6 convoys; 48 clock cycles The count without chaining — and also the count if you pack strictly in listing order, so that MULVS.D V1 waits behind L.V V2 and L.V V3. With chaining and the packing of move 3, four convoys suffice.
Show model answer

Answer 3: 4 convoys; 4 × 8 = 32 clock cycles (8 elements, one 64-bit element per cycle).

June 2025, Question 4 (recomputed).
Load/Store Vector UnitMul/Div Vector UnitAdd/Sub Vector Unit
1^ convoyL.V V1, RAMULVS.D V1, V1, F1
2^ convoyL.V V2, RBMULVS.D V2, V2, F2ADDVV.D V2, V2, V1
3^ convoyL.V V3, RCMULVS.D V3, V3, F3ADDVV.D V3, V2, V3
4^ convoyS.V V3, RD
5^ convoy
6^ convoy

The single load/store unit serialises the three loads and the store; with chaining each multiply joins the convoy of its load, and each add the convoy of its multiply, its other operand being complete (V1 after the 1^ convoy, V2 after the 2^).

The paper fills in L.V V1, RA and leaves six rows: two stay empty here. MULVS.D V1 joins the first convoy although two loads stand before it in the listing, because it does not depend on them — the packing of move 3. Answer 5, 6 convoys and 48 cycles, is the count without chaining and also the count of a packing that keeps strict listing order. No official key exists for this paper: under the table, write the one-line assumption that independent instructions are grouped regardless of listing order.

Let's consider the following code: for (i=0; i<260; i++) Y[i] = X[i] + Y[i]; Which code transformation can be applied to be executed by the VMIPS processor with a Vector Register File composed of 8 registers of 64 elements and 64 bits/element? Motivate your answer.

  • Trace scheduling A VLIW compiler technique that schedules scalar instructions across branches; this loop body has no branch, and its problem is the vector length.
  • Software pipelining It overlaps iterations of a scalar loop on a statically scheduled machine; it does not make 260 elements fit into 64-element registers.
  • Strip mining 260 > 64, so the loop is cut into strips of at most 64 elements, with the vector length register set for each strip.
  • Apply a mask register A mask handles a conditional statement inside a vector loop; there is none here.
Show model answer

Strip mining. MVL = 64 < n = 260, so the loop is split into ⌈260/64⌉ = 5 strips: four strips of 64 elements and one of 260 mod 64 = 4 elements (first, in Hennessy and Patterson's code, or last), setting VLR before each strip. Each strip executes L.V V1, RX; L.V V2, RY; ADDVV.D V3, V1, V2; S.V V3, RY. A mask register is for an IF inside the loop, which is absent; trace scheduling and software pipelining are VLIW compiler techniques for scheduling scalar instructions.

Question 5 of July 2024; June 2026 asked it again with 16 registers of 32 elements (260 = 8 × 32 + 4). The motivation point is the arithmetic, plus one sentence on why each distractor does not fit.

Let's consider the following code executed by a Vector Processor with: Vector Register File composed of 32 vectors of 16 elements per 32 bits/element; Scalar FP Register File composed of 32 registers of 32 bits; One Load/Store Vector Unit with memory bandwidth 32 bits; One Add/Sub Vector Unit; One Mul/Div Vector Unit. None of the three units has operation chaining.

L.V      V1, RX
ADDVS.D  V1, V1, F0
MULV.D   V2, V1, V1
DIVVS.D  V2, V2, F0
L.V      V3, RY
ADDVV.D  V3, V2, V3
S.V      V2, RZ
S.V      V3, RW

How many clock cycles does the code take?

Show model answer

Without chaining every true dependence ends a convoy, and the chain L.V V1 → ADDVS.D V1 → MULV.D V2 → DIVVS.D V2 → ADDVV.D V3 → S.V V3 has six instructions: 6 convoys. A convoy lasts 16 cycles (16 elements, one 32-bit element per cycle): 6 × 16 = 96 clock cycles.

January 2026 code without chaining (recomputed).
Load/Store Vector UnitMul/Div Vector UnitAdd/Sub Vector Unit
1^ convoyL.V V1, RX
2^ convoyL.V V3, RYADDVS.D V1, V1, F0
3^ convoyMULV.D V2, V1, V1
4^ convoyDIVVS.D V2, V2, F0
5^ convoyS.V V2, RZADDVV.D V3, V2, V3
6^ convoyS.V V3, RW

With chaining, as in the January 2026 paper: 4 convoys, 64 cycles — L.V V1 + ADDVS.D + MULV.D; L.V V3 + DIVVS.D + ADDVV.D; S.V V2; S.V V3.

The code is the January 2026 quiz (notes, p. 501) with chaining removed. Without chaining the floor from the load/store unit (4) is not the answer: the chain of six dependent instructions is longer. Note that the elements are 32 bits here and the bandwidth 32 bits: still one element per cycle.

A vector processor has MVL = 64, 4 lanes, one Load/Store Vector Unit that moves 4 elements per cycle, one MUL/DIV and one ADD/SUB Vector Unit, all with operation chaining. The DAXPY loop Y[i] = a * X[i] + Y[i] runs on n = 1000 elements with strip mining; each strip executes L.V, MULVS.D, L.V, ADDVV.D, S.V. Ignoring start-up and the scalar loop overhead, how many clock cycles does the loop take?

Show model answer

Each strip: 3 convoys (L.V + MULVS.D; L.V + ADDVV.D; S.V). Strips: ⌈1000/64⌉ = 16 — 15 of 64 elements and one of 1000 mod 64 = 40. With 4 lanes a chime takes ⌈VL/4⌉ cycles: 16 for a full strip, 10 for the short one.

\[ T = 15 \times 3 \times 16 + 3 \times 10 \] \[ T = 720 + 30 = 750 \]

750 clock cycles, 0.75 per element.

Two traps: forgetting the short strip (15 × 48 = 720) and charging it a full chime (16 × 48 = 768). Lanes divide the chime, strips multiply the convoys.

Check yourself

VMIPS with one lane runs a sequence packed into 3 convoys on vectors of 64 elements. How long is a chime, and how long is the sequence?

  • 1 clock cycle; 3 cycles in all One cycle in which every unit handles one element is the notes' "tick" — a clock cycle, not a chime. A convoy streams all 64 elements through its units.
  • 64 clock cycles; 192 cycles in all A chime is one convoy's time, VL = 64 cycles, and 3 convoys take 3 × 64 = 192 cycles.
  • 3 clock cycles; 64 cycles in all Convoys run one after the other, they do not divide the vector length.
  • 64 clock cycles per instruction; 320 cycles for DAXPY's five instructions The instructions of one convoy overlap: time is counted per convoy, not per instruction.

The July 2024 / June 2026 code needs 4 convoys with chaining. Which changes would reduce the number of convoys? Select all that apply.

  • A second load/store vector unit Two memory instructions per convoy: the floor drops to 2, and 2 convoys are reachable — 2 × 16 = 32 cycles.
  • Keeping V3 in its register instead of storing it (no S.V V3, R2) Three memory instructions: L.V V1 with its two chained instructions, L.V V3 with its add, S.V V2 — 3 convoys.
  • A second ADD/SUB unit The four memory instructions still share one load/store unit: still 4 convoys.
  • Four lanes Lanes shorten each chime to ⌈16/4⌉ = 4 cycles (16 cycles in all) but leave the convoy count at 4.
  • Switching chaining off More convoys, not fewer: 5.

With chaining on, only the busiest unit matters: change its load of work, and the count changes.

A vector unit with 4 lanes executes ADDVV.D V3, V1, V2 on 64 elements. Which lane computes element 13, and how many cycles does the instruction take, start-up ignored?

  • Lane 1; 16 cycles 13 mod 4 = 1, and ⌈64/4⌉ = 16.
  • Lane 3; 16 cycles 13 = 3 × 4 + 1: the remainder, 1, is the lane; 3 is the cycle in which that lane reaches it (counting from 0).
  • Lane 1; 64 cycles Four lanes take four elements per cycle: the chime shrinks to 16.
  • All four lanes share element 13; 4 cycles Each element lives in exactly one lane; lanes divide the elements, not each element.

A loop of 200 iterations runs on a vector machine with MVL = 64. How many strips, and what is VLR in the short one?

  • 3 strips, VLR = 64 in each 3 × 64 = 192: eight iterations would be lost.
  • 4 strips; VLR = 8 in the short one 200 = 3 × 64 + 8: ⌈200/64⌉ = 4 strips.
  • 4 strips; VLR = 56 in the short one 56 = 4 × 64 − 200 is the unused part of the last register; VLR counts the elements processed.
  • 3 strips: VLR = 64, 64, 72 VLR can never exceed MVL: 0 ≤ VLR ≤ MVL.

SUBVV.D V1, V1, V2 runs under a mask with 16 of its 64 bits set, on one lane. How long does it take in the chime model, and what happens to the 48 masked elements?

  • 64 cycles; they are left unchanged Masking decides which elements are written, not how many pass through the unit.
  • 16 cycles; they are skipped In the basic implementation of the notes every element still takes its cycle (p. 409).
  • 64 cycles; they are set to zero Masked elements keep their old value: X[i] stays X[i] when the if fails.
  • 16 cycles; they are left unchanged Right about the values, wrong about the time: the mask preserves vectorizability, not speed.

A matrix of 64-bit words is stored by rows, 128 words per row, in a memory of 8 banks with a busy time of 6 cycles. A vector loop walks one column (stride 128). Which changes remove the bank conflicts? Select all that apply.

  • Pad each row to 129 words gcd(129, 8) = 1: all 8 banks, and 8 ≥ 6 cycles of busy time.
  • Pad each row to 136 words 136 = 17 × 8, so gcd(136, 8) = 8: still one bank. The padding must make the stride coprime with the number of banks.
  • Interchange the loops so that the vector walks along a row Along a row the stride is 1: the loop interchange of lesson 25, this time for the banks.
  • Use 16 banks instead of 8 gcd(128, 16) = 16: one bank out of sixteen. More banks do not cure a power-of-two stride.
  • Store the matrix by columns Then a column is contiguous, stride 1 for this loop — and a loop over rows would now suffer instead.

On a machine without chaining, when can ADDVV.D V4, V2, V3 start, if V2 comes from MULVS.D V2, V1, F0?

  • When MULVS.D has written the last element of V2 — in a later convoy Without chaining the consumer reads a vector register only once it is complete.
  • As soon as MULVS.D has produced the first element of V2 That is exactly what chaining adds.
  • In the same cycle as MULVS.D, since they use different units Different units allow the same convoy only to independent instructions, or to chained ones.

Key take-aways

What to watch and read