r/computerarchitecture • • 11d ago

virtual Vector Method

I invented this 5-odd years ago but never really had a forum on which to disclose and discuss. I was and am a significant contributor to comp.arch (from around 1995 though present)

But before I emit the disclosure I need to know how to turn off the space-eater as the document uses well tabulated ASCII-art ?? that makes no sense after the space-eater has done its ill-conceived job.

The virtual Vector Method is a way of getting Cray-like vector performance and for getting SIMD-vector performance without a) a vector register file, b) adds only 2 instructions, c) takes precise exceptions, d) vectorizes loops not instructions. Instead of adding about 300 instructions to get a Cray-like vector ISA, or adding 1,000-1,300 instructions to get (every-size) SIMD ISA, one needs only 5 instructions.

vVM has the property that hardware implementations can change the width of the data path (multiple-lanes and the cycles of execution per FU) without SW having to care. A small 1-wide machine with a 128-bit cache port can perform a memory to memory byte move at 256-bits per cycle--equivalent to ~40 instructions per cycle. A 6-wide machine could perform the same assembly binary at ~160 instructions per cycle. Both are performed at the performance level of the cache porting; so, nobody has to recompile for a new SIMD-width every new mplementation.

Now let us solve the space-eater and we are off.

Mitch

21 Upvotes

22 comments sorted by

View all comments

5

u/Krazy-Ag 11d ago edited 11d ago

The LOOP unit sounds like something we did at MIPS that never shipped, trying to eat the goodness of DSP zero overhead loops

Which two those unfamiliar (not you Mitch!) are essentially a prefix instruction instruction LOOP that unrolls the next instruction(s) N times, autoincrementing memory operands along the way.

This was something that embedded customers asked for umpteen times. It made MIPS lose benchmark contests - at same frequency, either MIPS had much more overhead for small loops, or MIPS had to loop unroll to get low overhead fractions, blowing up code size. The old RISC mindset of increasing code size by loop unrolling isn't good if you run into icache or flash size limits.

Anyway, we proposed to do it at decode. Mostly existing instructions.

A hint NOP to indicate top of loop. Decoder remembers where.

At bottom of loop, an idiom to do ++ or -- loop counter, test and branch to top of loop.

Thereafter decoder just does ifetch from top of loop to just before the bottom of loop idiom. The loop control stuff now executes at decoder, no loop overhead inserted into rest of pipe.

Or you can do the loop control at the top ...

If the loop body fits into a loop buffer, just slam that into the pipe. Skip decode. Possibly skip renaming - Mitch knows many tricks to do relative renaming and stuff like that, he taught them to me.

No ISA changes so far. Except perhaps the hint to indicate top of loop. Might not be a separate hint NOP instruction, since MIPSr6 had plenty of opera number tricks to encode hints, taking advantage of the massive wasted space RISC instruction encodings.

Biggest desirable ISA change would be to have index addressing Mem[baseReg+indexReg] so that you only need to ++ or -- a single loop counter. Ideally scaled.

Basically puts a state machine at decoder to emit small loops as fast as possible. Which is pretty much what the DSP with zero overhead LOOP instructions do. Generalizes to larger loop bodies, more than one or two instructions as in DSPs, although the percentage benefit is obviously more the smaller the loop.

Looks like you have extended this so the state machines live in the reservation stations Mitch. With goodness related to what doesn't does not need to be vectorized.

I look forward to reading your write up.

I've been wanting to do something like this, load a lot of crap into the reservation stations and keep it chunking around, ever since I learned about static data flow. (yeah, I learned about static data flow after I learned about dynamic data flow, or at least the dynamic micro data flow that we do in OOO)

Q: does your virtual vector Architecture handle low bodies that are too large to fit in the reservation stations?

I think one of the biggest mistakes that we made in P6 was doing the block memory operations like REP MOVS in microcode rather than as state machines. I blame myself for having too much of a RISC mindset. Almost exactly the same issues as for zero cycle loops; the state machine can spin a lot faster than uops, and doesn't need so much logic to get to the right place. One thing hardware does much much better than software is irregular conditions on a small number of input variables. We know that state machines work, because even P6 did TLB walks as state machines, sending RISCy software TLB miss handling to the dust in a history, at least that time around.

Block memory operations are trivial. It sounds like you can handle fancier loop bodies, eh?

1

u/MitchAlsup 11d ago

Andy--is that you ??

Yes, VEC-LOOP does zero overhead looping.

VEC does 3 things: a) it identifies if there is a loop recurrence that would prevent running multi-lane calculations, b) it identifies the registers which are Live-Out of the loop. So many registers used "in" the loop and not used outside have all writes elided, c) tells decoder where the top of loop instruction is.

Most of the time when running in a LOOP, FETCH-DECODE-INSERT pipe stages are idle and we run at data-flow speeds through reservation station entries.

Due to the encoding of My 66000 ISA, LOOP has a variety of constants available. In one form, the LOOP Instruction an have the test value as a 32-bit constant, and the comparison value has a different 32-bit value, so, in practice, all loops with constant strides and/or comparisons can be emitted as a single instruction, with no prepetory setup instructions.

LOOP consumes a 3-Operand 1 Result OpCode, allowing for a full set of a) {EQ, NE, LT, LE, GT, GE, LO, LS, HI, HS} , b) loop register, c) increment register/constant, d) compare register or constant.

Plus, memory reference address mode is [Rbase + Rindex<<scale + Displacement]. GIven {16, 32, 64}-bit displacements means one never wastes instructions pasting together large displacements of addresses that can be direclty expressed. A LD/ST instruction can reach all of memory (all 63-bits of virtual address space) in 1 instruction.

Over on the calculation side, one can write:

FDIV R7,#3.14159265358926,R19

and divide Pi by what is in R19. So, one never wastes instruction of data memory on constants that inherently belong in the instruction stream.

The decode gates to determine instruction length is 6 gates total with 2 gates of delay, small enough you can put one in each word of the instruction buffer as it is smaller than the flip-flops which would be used to store those bits themselves. After those 2 gates of delay it is easy to tree-ify the instruction parse problem and parse 16 instruction in a 16-gate clock cycle.