Normal quotes
This commit is contained in:
parent
5e10794a24
commit
18011c595a
352 changed files with 6040 additions and 6040 deletions
40
63-02.md
40
63-02.md
|
|
@ -4,44 +4,44 @@
|
|||
|
||||
FDIV is a painfully slow instruction, taking 39 cycles at full precision
|
||||
and 33 cycles at double precision, which is the default precision for
|
||||
Visual C++ 2.0. While FDIV executes, the FPU is occupied, and can’t
|
||||
Visual C++ 2.0. While FDIV executes, the FPU is occupied, and can't
|
||||
process subsequent FP instructions until FDIV finishes. However, during
|
||||
the cycles while FDIV is executing (with the exception of the one cycle
|
||||
during which FDIV starts), the integer unit can simultaneously execute
|
||||
instructions other than IMUL. (IMUL uses the FPU, and can only overlap
|
||||
with FDIV for a few cycles.) Since the integer unit can execute two
|
||||
instructions per cycle, this means it’s possible to have three
|
||||
instructions per cycle, this means it's possible to have three
|
||||
instructions, an FDIV and two integer instructions, executing at the
|
||||
same time. That’s exactly what happens, for example, during the second
|
||||
same time. That's exactly what happens, for example, during the second
|
||||
cycle of this code:
|
||||
|
||||
FDIV ST(0),ST(1)
|
||||
ADD EAX,ECX
|
||||
INC EDX
|
||||
|
||||
There’s an important limitation, though; if the instruction stream
|
||||
There's an important limitation, though; if the instruction stream
|
||||
following the FDIV reaches a FP instruction (or an IMUL), then that
|
||||
instruction and all subsequent instructions, both integer and FP, must
|
||||
wait to execute until FDIV has finished.
|
||||
|
||||
When a FADD, FSUB, or FMUL instruction is executed, it is 3 cycles
|
||||
before the result can be used by another instruction. (There’s an
|
||||
before the result can be used by another instruction. (There's an
|
||||
exception: If the instruction that attempts to use the result is an FST
|
||||
to memory, there’s an extra cycle lost, so it’s 4 cycles from the start
|
||||
to memory, there's an extra cycle lost, so it's 4 cycles from the start
|
||||
of an arithmetic instruction until an FST of that value can begin, so
|
||||
|
||||
FMUL ST(0),ST(1)
|
||||
FST [temp]
|
||||
|
||||
takes 6 cycles in all.) Again, it’s possible to execute integer-unit
|
||||
takes 6 cycles in all.) Again, it's possible to execute integer-unit
|
||||
instructions during the 2 (or 3, for FST) cycles after one of these FP
|
||||
instructions starts. There’s a more exciting possibility here, though:
|
||||
instructions starts. There's a more exciting possibility here, though:
|
||||
Given properly structured code, the FPU is capable of averaging 1 cycle
|
||||
per FADD, FSUB, or FMUL. The secret is pipelining.
|
||||
|
||||
#### Pipelining, Latency, and Throughput {#Heading5}
|
||||
|
||||
The Pentium’s FPU is the first pipelined x86 FPU. *Pipelining* means
|
||||
The Pentium's FPU is the first pipelined x86 FPU. *Pipelining* means
|
||||
that the FPU is capable of starting an instruction every cycle, and can
|
||||
simultaneously handle several instructions in various stages of
|
||||
completion. Only certain x86 FP instructions allow another instruction
|
||||
|
|
@ -57,11 +57,11 @@ is not an issue.) Thus, in the code sequence
|
|||
FADD~1~ can start on cycle N, FSUB can start on cycle N+1, FADD~2~ can
|
||||
start on cycle N+2, and FMUL can start on cycle N+3. At the start of
|
||||
cycle N+3, the result of FADD~1~ is available in the destination
|
||||
operand, because it’s been 3 cycles since the instruction started; FSUB
|
||||
operand, because it's been 3 cycles since the instruction started; FSUB
|
||||
is starting the final cycle of calculation; FADD~2~ is starting its
|
||||
second cycle, with one cycle yet to go after this; and FMUL is about to
|
||||
be issued. Each of the instructions takes 3 cycles to produce a result
|
||||
from the time it starts, but because they’re simultaneously processed at
|
||||
from the time it starts, but because they're simultaneously processed at
|
||||
different pipeline stages, one instruction is issued and one instruction
|
||||
completes every cycle. Thus, the latency of these instructions—that is,
|
||||
the time until the result is available—is 3 cycles, but the
|
||||
|
|
@ -72,14 +72,14 @@ every 2 cycles, so between these two instructions
|
|||
FMUL ST(1),ST(0)
|
||||
FMUL ST(2),ST(0)
|
||||
|
||||
there’s a 1-cycle stall, and the following three instructions execute
|
||||
there's a 1-cycle stall, and the following three instructions execute
|
||||
just as fast as the above pair:
|
||||
|
||||
FMUL ST(1),ST(0)
|
||||
FLD ST(4)
|
||||
FMUL ST(0),ST(1)
|
||||
|
||||
There’s a caveat here, though: A FP instruction can’t be issued until
|
||||
There's a caveat here, though: A FP instruction can't be issued until
|
||||
its operands are available. The FPU can reach a throughput of 1 cycle
|
||||
per instruction on this code
|
||||
|
||||
|
|
@ -93,7 +93,7 @@ Consider, however
|
|||
FADD ST(0),ST(2)
|
||||
FSUB ST(0),ST(1)
|
||||
|
||||
where the ST(0) operand to FSUB is calculated by FADD. Here, FSUB can’t
|
||||
where the ST(0) operand to FSUB is calculated by FADD. Here, FSUB can't
|
||||
start until FADD has completed, so there are 2 stall cycles between the
|
||||
two instructions. When dependencies like this occur, the FPU runs at
|
||||
latency rather than throughput speeds, and performance can drop by as
|
||||
|
|
@ -110,12 +110,12 @@ ST(0), and FST can only store from ST(0). How, then, can we keep three
|
|||
independent instructions going?
|
||||
|
||||
The easy answer would be for Intel to change the FP registers from a
|
||||
stack to a set of independent registers. Since they couldn’t do that,
|
||||
stack to a set of independent registers. Since they couldn't do that,
|
||||
thanks to compatibility issues, they did the next best thing: They made
|
||||
the FXCH instruction, which swaps ST(0) and any other FP register,
|
||||
virtually free. In general, if FXCH is both preceded and followed by FP
|
||||
instructions, then it takes *no* cycles to execute. (Application Note
|
||||
500, “Optimizations for Intel’s 32-bit Processors,” February 1994,
|
||||
500, "Optimizations for Intel's 32-bit Processors," February 1994,
|
||||
available from [http://www.intel.com](http://www.intel.com), describes
|
||||
all .the conditions under which FXCH is free.) This allows you to move
|
||||
the target of a pending operation from ST(0) to another register, at the
|
||||
|
|
@ -138,7 +138,7 @@ multiplications, without incurring any stalls, as shown in Listing 63.1.
|
|||
|
||||
### The Dot Product {#Heading7}
|
||||
|
||||
Now we’re ready to look at fast FP for common 3-D operations; we’ll
|
||||
Now we're ready to look at fast FP for common 3-D operations; we'll
|
||||
start by looking at how to speed up the dot product. As discussed in
|
||||
Chapter 30, the dot product is heavily used in 3-D to calculate cosines
|
||||
and to project points along vectors. The dot product is calculated as d
|
||||
|
|
@ -152,9 +152,9 @@ by doing all three FMULs first, then using FXCH to set the third FXCH
|
|||
aside to complete while the results of the first two FMULs, which have
|
||||
completed, are added. Listing 43.3 still loses 50 percent to stalls, but
|
||||
unless some other code is available to be interleaved with the dot
|
||||
product code, that’s all we can do to speed things up. Fortunately, dot
|
||||
products are often used in contexts where there’s plenty of interleaving
|
||||
potential, as we’ll see when we discuss transformation.
|
||||
product code, that's all we can do to speed things up. Fortunately, dot
|
||||
products are often used in contexts where there's plenty of interleaving
|
||||
potential, as we'll see when we discuss transformation.
|
||||
|
||||
------------------------ --------------------------------- --------------------
|
||||
[Previous](63-01.html) [Table of Contents](index.html) [Next](63-03.html)
|
||||
|
|
|
|||
Loading…
Reference in a new issue