Normal quotes

This commit is contained in:
James Gregory 2013-12-30 18:34:31 +11:00
commit 18011c595a
352 changed files with 6040 additions and 6040 deletions

View file

@ -2,15 +2,15 @@
[Previous](09-01.html) [Table of Contents](index.html) [Next](09-03.html)
------------------------ --------------------------------- --------------------
However, it generally is. Sure, if the length is odd, John’s approach
However, it generally is. Sure, if the length is odd, John's approach
incurs a penalty approximately equal to the **REP** startup time for
**MOVSB**. However, if the length is even, John’s approach doesn’t
**MOVSB**. However, if the length is even, John's approach doesn't
branch, saving cycles and not emptying the prefetch queue. If copy
lengths are evenly distributed between even and odd, John’s approach is
lengths are evenly distributed between even and odd, John's approach is
faster in most x86 systems. (Not on the 486, though.)
John also points out that on the 386, multiple **LEA**s can be combined
to perform multiplications that can’t be handled by a single **LEA**,
to perform multiplications that can't be handled by a single **LEA**,
much as multiple shifts and adds can be used for multiplication, only
faster. **LEA** can be used to multiply in a single instruction on the
386, but only by the values 2, 3, 4, 5, 8, and 9; several **LEA**s
@ -34,7 +34,7 @@ Using **LEA** on the 386, the above could be reduced to
LEA EAX,[EAX*8] ;*16
LEA EAX,[EAX+EAX*4] ;*80
which still isn’t as fast as using a lookup table like
which still isn't as fast as using a lookup table like
MOV EAX,MultiplesOf80Table[EAX*4]
@ -55,7 +55,7 @@ and one that allows for considerable optimization. One way to speed up
multiplication is to use shift and add, **LEA**, or a lookup table to
hard-code a multiplication operation for a fixed multiplier, as shown
above. Another is to take advantage of the early-out feature of the 386
(and the 486, but in the interests of brevity I’ll just say “386” from
(and the 486, but in the interests of brevity I'll just say "386" from
now on) by arranging your operands so that the multiplier (always the
rightmost operand following **MUL** or **IMUL**) is no larger than the
other operand.
@ -64,7 +64,7 @@ other operand.
![](images/i.jpg) *Why? Because the 386 processes one multiplier bit per cycle and immediately ends a multiplication when all significant bits of the multiplier have been processed, so fewer cycles are required to multiply a large multiplicand times a small multiplier than a small multiplicand times a large multiplier, by a factor of about 1 cycle for each significant multiplier bit eliminated.*
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
(There’s a minimum execution time on this trick; below 3 significant
(There's a minimum execution time on this trick; below 3 significant
multiplier bits, no additional cycles are saved.) For example,
multiplication of 32,767 times 1 is 12 cycles faster than multiplication
of 1 times 32,727.
@ -82,7 +82,7 @@ This highlights another interesting point: **MUL** and **IMUL** on the
generally still faster, are worthwhile only in truly time-critical code.
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
![](images/i.jpg) *On 386SXs and uncached 386s, where code size can significantly affect performance due to instruction prefetching, the compact **MUL** and **IMUL** instructions can approach and in some cases even outperform the “optimized” alternatives.*
![](images/i.jpg) *On 386SXs and uncached 386s, where code size can significantly affect performance due to instruction prefetching, the compact **MUL** and **IMUL** instructions can approach and in some cases even outperform the "optimized" alternatives.*
------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
All in all, **MUL** and **IMUL** are reasonable performers on the 386,
@ -90,10 +90,10 @@ no longer to be avoided in most cases—and you can help that along by
arranging your code to make the smaller operand the multiplier whenever
you know which operand is smaller.
That doesn’t mean that your code should test and swap operands to make
sure the smaller one is the multiplier; that rarely pays off. I’m
speaking more of the case where you’re scaling an array up by a value
that’s always in the range of, say, 2 to 10; because the scale value
That doesn't mean that your code should test and swap operands to make
sure the smaller one is the multiplier; that rarely pays off. I'm
speaking more of the case where you're scaling an array up by a value
that's always in the range of, say, 2 to 10; because the scale value
will always be small and the array elements may have any value, the
scale value is the logical choice for the multiplier.
@ -105,7 +105,7 @@ quick refresher, I described searching a buffer for a text string as
follows: Scan for the first byte of the text string with **REPNZ
SCASB**, then use **REPZ CMPS** to check for a full match whenever
**REPNZ SCASB** finds a match for the first character, as shown in
Figure 9.1. The principle is that most buffer characters won’t match the
Figure 9.1. The principle is that most buffer characters won't match the
first character of any given string, so **REPNZ SCASB**, by far the
fastest way to search on the PC, can be used to eliminate most potential
matches; each remaining potential match can then be checked in its
@ -114,7 +114,7 @@ entirety with **REPZ CMPS**.
![](images/09-01.jpg)\
**Figure 9.1**  *Simple searching method for locating a text string.*
Rob’s revelation, which he credits without explanation to Edgar Allen
Rob's revelation, which he credits without explanation to Edgar Allen
Poe (search nevermore?), was that by far the slowest part of the whole
deal is handling **REPNZ SCASB** matches, which require checking the
remainder of the string with **REPZ CMPS** and restarting **REPNZ
@ -124,11 +124,11 @@ SCASB** if no match is found.
![](images/i.jpg) *Rob points out that the number of **REPNZ SCASB** matches can easily be reduced simply by scanning for the character in the searched-for string that appears least often in the buffer being searched.*
------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Imagine, if you will, that you’re searching for the string “EQUAL.” By
my approach, you’d use **REPNZ SCASB** to scan for each occurrence of
“E,” which crops up quite often in normal text. Rob points out that it
would make more sense to scan for “Q,” then back up one character and
check the whole string when a “Q” is found, as shown in Figure 9.2. “Q”
Imagine, if you will, that you're searching for the string "EQUAL." By
my approach, you'd use **REPNZ SCASB** to scan for each occurrence of
"E," which crops up quite often in normal text. Rob points out that it
would make more sense to scan for "Q," then back up one character and
check the whole string when a "Q" is found, as shown in Figure 9.2. "Q"
is likely to occur much less often, resulting in many fewer whole-string
checks and much faster processing.