abrash-black-book/21-02.md
2013-12-30 20:26:41 +11:00

158 lines
6.5 KiB
Markdown

As on the 486, you should keep a careful eye out for AGIs involving the
stack pointer. Implicit modifiers of ESP, such as **PUSH** and **POP**,
are special-cased so you don't have to worry about AGIs. However, if you
explicitly modify ESP with this instruction
sub esp,100h
for example, or with the popular
mov esp,ebp
you can then get AGIs if you attempt to use ESP to address memory,
either explicitly with instructions like this one
moveax,[esp+20h]
or via **PUSH**, **POP**, or other instructions that implicitly use ESP
as an addressing register.
On the 486, any instruction that had both a constant value and an
addressing displacement, such as
mov dword ptr [ebp+16],1
suffered a 1-cycle penalty, taking a total of 2 cycles. Such
instructions take only one cycle on the Pentium, but they cannot pair,
so they're still the most expensive sort of **MOV**. Knowing this can
speed up something as simple as zeroing two memory variables, as in
sub eax,eax ;U-pipe 1
;any V-pipe pairable
; instruction can go here,
; or SUB could be in V-pipe
mov [MemVar1],eax ;U-pipe 2
mov [MemVar2],eax ;V-pipe 2
which should never be slower and should potentially be 0.5 cycles
faster, and six bytes smaller than this sequence:
mov [MemVar1],0 ;U-pipe 1
mov [MemVar2],0 ;U-pipe 2
Note, however, that my experiments thus far indicate that the two writes
in the first case don't actually pair (possibly because the memory
variables have never been read into the internal cache), so you might
want to insert an instruction between the two **MOV**s—and, of course,
this is yet another reason why you should always measure your code's
actual performance.
### Register Contention {#Heading4}
Finally, we come to the last major component of superscalar
optimization: register contention. The basic premise here is simple: You
can't use the same register in two inherently sequential ways in a
single cycle. For example, you can't execute
inc eax ;U-pipe cycle 1
;V-pipe idle cycle 1
; due to dependency
and ebx,eax ;U-pipe cycle 2
in a single cycle; **AND EBX,EAX** can't execute until the value in EAX
is known, and that can't happen until **INC EAX** is done. Consequently,
the V-pipe idles while **INC EAX** executes in the U-pipe. We saw this
in the last chapter when we discussed splitting instructions into simple
instructions, and it is by far the most common sort of register
contention, known as read-after-write register contention.
Read-after-write register contention is the primary reason we have to
interleave independent operations in order to get maximum V-pipe usage.
The other sort of register contention is known as write-after-write.
Write-after-write register contention happens when two instructions try
to write to the same register on the same cycle. While that may not seem
like a particularly useful operation in general, it can happen when
subregisters are being set, as in the following
sub eax,eax ;U-pipe cycle 1
;V-pipe idle cycle 1
; due to register contention
mov al,[Var] ;U-pipe cycle 2
where an attempt is made to set both EAX and its AL subregister on the
same cycle. Write-after-write contention implies that the two
instructions comprising the above substitute for **MOVZX** should have
at least one unrelated instruction between them when **SUB EAX,EAX**
executes in the V-pipe.
#### Exceptions to Register Contention {#Heading5}
Intel has special-cased some very useful exceptions to register
contention. Happily, write-after-read operations do *not* cause
contention. Such operations, as in
mov eax,edx ;U-pipe cycle 1
sub edx,edxX ;V-pipe cycle 1
are free of charge.
Also, stack-related instructions that modify ESP only implicitly
(without ESP as part of any explicit operand) do not cause AGIs, and
neither do they cause register contention with other instructions that
use ESP only implicitly; such instructions include **PUSH *reg/immed*,
POP *reg***, and **CALL**. (However, these instructions do cause
register contention on ESP—but not AGIs—with instructions that use ESP
explicitly, such as **MOV EAX,[ESP+4]**.) Without this special case, the
following sequence would hardly use the V-pipe at all:
mov eax,[MemVar] ;U-pipe cycle 1
push esi ;V-pipe cycle 1
push eax ;U-pipe cycle 2
push edi ;V-pipe cycle 2
push ebx ;U-pipe cycle 3
call FooTilde ;V-pipe cycle 3
But in fact, all the instructions pair, even though ESP is modified five
times in the space of six instructions.
The final register-contention special case is both remarkable and
remarkably important. There is exactly one sort of instruction that can
pair only in the V-pipe: branches. Any near call or conditional or
unconditional near jump can execute in the V-pipe paired with any
pairable U-pipe instruction, as illustrated by this sequence:
LoopTop:
mov [esi],eax ;U-pipe cycle 1
add esi,4 ;V-pipe cycle 1
dec ecx ;U-pipe cycle 2
jnz LoopTop ;V-pipe cycle 2
Branches can't pair in the U-pipe; a branch that executes in the U-pipe
runs alone, with the V-pipe idle. If a call or jump is correctly
predicted by the Pentium's branch prediction circuitry (as discussed in
the last chapter), it executes in a single cycle, pairing if it runs in
the V-pipe; if mispredicted, conditional jumps take 4 cycles in the
U-pipe and 5 cycles in the V-pipe, and mispredicted calls and
unconditional jumps take 3 cycles in either pipe. Note that **RET**
can't pair.
### Who's in First? {#Heading6}
One of the trickiest things about superscalar optimization is that a
given instruction stream can execute at a different speed depending on
the pipe where it starts execution, because which instruction goes
through the U-pipe first determines which of the following instructions
will be able to pair. If we take the last example and add one more
instruction, the other instructions will go through different pipes than
previously, and cause the loop as a whole to take 50 percent longer,
even though we only added 25 percent more cycles:
LoopTop:
inc edx ;U-pipe cycle 1
mov [esi],eax ;V-pipe cycle 1
add esi,4 ;U-pipe cycle 2
dec ecx ;V-pipe cycle 2
jnz LoopTop ;U-pipe cycle 3
;V-pipe idle cycle 3
; because JNZ can't
; pair in the U-pipe