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

@ -6,13 +6,13 @@
ScanLoop:
lodsw ;get the next 2 bytes (AL = first, AH = 2nd)
xlat ;look up first’s char/not status
xor dl,al ;see if there’s a new char/not status
xlat ;look up first's char/not status
xor dl,al ;see if there's a new char/not status
add di,dx ;we add 1 for each char/not transition
mov dl,al
mov al,ah ;look at the second byte
xlat ;look up its char/not status
xor dl,al ;see if there’s a new char/not status
xor dl,al ;see if there's a new char/not status
add di,dx ;we add 1 for each char/not transition
mov dl,al
dec dx
@ -20,10 +20,10 @@
John later divides the transition count by two to get the word count.
(Food for thought: It’s also possible to use **CMP** and **ADC** to
(Food for thought: It's also possible to use **CMP** and **ADC** to
detect words without branching.)
John’s approach makes it clear that word-counting is nothing more than a
John's approach makes it clear that word-counting is nothing more than a
fairly simple state machine. The interesting part, of course, is
building the fastest state machine.
@ -40,21 +40,21 @@ In the case of word counting, level 3 means building a table-driven
state machine dedicated to processing a buffer of bytes into a count of
words with a minimum of branching. This level of optimization strips
away many of the abstractions we usually use in coding, such as loops,
tests, and named variables—look back to Listing 16.5, and you’ll see
what I mean. Only a few people reached this level, and I don’t think any
of them did it without long, hard thinking; David Stafford’s final entry
tests, and named variables—look back to Listing 16.5, and you'll see
what I mean. Only a few people reached this level, and I don't think any
of them did it without long, hard thinking; David Stafford's final entry
(that is, the one I present as Listing 16.5) was *at least* the fifth
entry he sent me.
The key concept at level 3 is the use of a massive (64K) lookup table
that processes byte sequences directly into word-count actions. With
such a table, it’s possible to look up the appropriate action for two
bytes simultaneously in just a few instructions; next, I’m going to look
at the inspired and highly unusual way that David’s code, shown in
such a table, it's possible to look up the appropriate action for two
bytes simultaneously in just a few instructions; next, I'm going to look
at the inspired and highly unusual way that David's code, shown in
Listing 16.5, does exactly that. (Before assembling Listing 16.5, you
must run the C code in Listing 16.8, to generate an include file
defining the 64K lookup table. When you assemble Listing 16.5, TASM will
report a “location counter overflow” warning; ignore it.)
report a "location counter overflow" warning; ignore it.)
**LISTING 16.8 MAKETAB.C**
@ -63,7 +63,7 @@ report a “location counter overflow” warning; ignore it.)
#include <stdio.h>
#include <ctype.h>
#define ChType( c ) (((c) & 0x7f) == ‘\’’ || isalnum((c) & 0x7f))
#define ChType( c ) (((c) & 0x7f) == ‘\'' || isalnum((c) & 0x7f))
int NoCarry[ 4 ] = { 0, 0x80, 1, 0x80 };
int Carry[ 4 ] = { 1, 0x81, 1, 0x80 };
@ -71,9 +71,9 @@ report a “location counter overflow” warning; ignore it.)
void main( void )
{
int ahChar, alChar, i;
FILE *t = fopen( “QSCAN3.INC”, “wt” );
FILE *t = fopen( "QSCAN3.INC", "wt" );
printf( “Building table. Please wait...” );
printf( "Building table. Please wait..." );
for( ahChar = 0; ahChar < 128; ahChar++ )
{
@ -81,10 +81,10 @@ report a “location counter overflow” warning; ignore it.)
{
i = ChType( alChar ) * 2 + ChType( ahChar );
if( alChar % 8 == 0 ) fprintf( t, “\ndb %02Xh”, NoCarry[ i ] );
else fprintf( t, “,%02Xh”, NoCarry[ i ] );
if( alChar % 8 == 0 ) fprintf( t, "\ndb %02Xh", NoCarry[ i ] );
else fprintf( t, ",%02Xh", NoCarry[ i ] );
fprintf( t, “,%02Xh”, Carry[ i ] );
fprintf( t, ",%02Xh", Carry[ i ] );
}
}
@ -92,7 +92,7 @@ report a “location counter overflow” warning; ignore it.)
}
David’s approach is simplicity itself, although his implementation
David's approach is simplicity itself, although his implementation
arguably is not. Consider any three sequential bytes in the buffer.
Those three bytes define two potential places where a word might be
counted, as shown in Figure 16.1. Given the separator/non-separator
@ -102,11 +102,11 @@ there is a non-separator followed by a separator. Note that a maximum of
one word can be counted per three-byte sequence.
The trick, then, is to identify the separator/not statuses of each set
of three bytes and turn them into a 1 (count word) or 0 (don’t count
of three bytes and turn them into a 1 (count word) or 0 (don't count
word), as quickly as possible. Assuming that the separator/not status
for the first byte is in the Carry flag, this is easily accomplished by
a lookup in a 64K table, based on the Carry flag and the other two
bytes, as shown in Figure 16.2. (Remember that we’re counting 7-bit
bytes, as shown in Figure 16.2. (Remember that we're counting 7-bit
ASCII here, so the high bit is ignored.) Thus, David is able to add the
word/not status for each pair of bytes to the main word count simply by
getting the two bytes, working in the carry status from the last byte,
@ -122,7 +122,7 @@ One detail remains to be attended to: setting the Carry flag for next
time if the last byte was a non-separator. David does this in a bizarre
and incredibly effective way: He presets the high bit of the count, and
sets the high bit in the lookup table for those entries looked up by
non-separators. When a non-separator’s lookup entry is added to the
non-separators. When a non-separator's lookup entry is added to the
count, it will produce a carry, as desired. The high bit of the count is
masked off before being added to the total count, so David is
essentially using different parts of the count variables for different
@ -131,13 +131,13 @@ purposes (counting, and setting the Carry flag).
![](images/16-02.jpg)\
**Figure 16.2**  *Looking up a word count status.*
There are a number of other interesting details in David’s code,
There are a number of other interesting details in David's code,
including the unrolling of the loop 64 times, so that 256 bytes in a row
are processed without a single branch. Unfortunately, I lack the space
to discuss Listing 16.5 any further. Perhaps that’s not so unfortunate,
after all; I’d hate to deny you the pleasure of discovering the wonders
to discuss Listing 16.5 any further. Perhaps that's not so unfortunate,
after all; I'd hate to deny you the pleasure of discovering the wonders
of this rather remarkable code yourself. I will say one more thing,
though. The cycle count for David’s inner loop is 6.5 cycles per byte
though. The cycle count for David's inner loop is 6.5 cycles per byte
processed, and the actual measured time for his routine, overhead and
all, is 7.9 cycles/byte. The original C code clocked in at around 100
cycles/byte.
@ -146,24 +146,24 @@ Enough said, I trust.
#### Enough Word Counting Already! {#Heading13}
Before I finish up this chapter, I’d like to mention that Terje
Mathisen’s WC word-counting program, which I’ve mentioned previously and
which is available, with source, on Bix, is in the ballpark with David’s
code for performance. What’s more, Terje’s program handles 8-bit ASCII,
Before I finish up this chapter, I'd like to mention that Terje
Mathisen's WC word-counting program, which I've mentioned previously and
which is available, with source, on Bix, is in the ballpark with David's
code for performance. What's more, Terje's program handles 8-bit ASCII,
counts lines as well as words, and supports user-definable separator
sets. It’s wonderful code, well worth a look; it also happens to be a
sets. It's wonderful code, well worth a look; it also happens to be a
great word-counting utility. By the way, Terje builds his 64K table on
the fly, at program initialization; this allows for customized tables,
shrinks the size of the EXE, and, according to Terje’s calculations,
shrinks the size of the EXE, and, according to Terje's calculations,
takes less time than loading the table off disk as part of the EXE.
So, has David written the fastest possible word-counting code? Well,
maybe—but I have a letter from Terry Holmes, of San Rafael, California,
that calculates the theoretical maximum performance of native 386
word-counting code at 5.5 cycles/byte, which would be significantly
faster than David’s code. Terry, alas, didn’t bother to implement his
design, but maybe I’ll take a shot at it someday. It’d be fun, for
sure—but jeez, I’ve got *real* work to do!
faster than David's code. Terry, alas, didn't bother to implement his
design, but maybe I'll take a shot at it someday. It'd be fun, for
sure—but jeez, I've got *real* work to do!
------------------------ --------------------------------- --------------------
[Previous](16-07.html) [Table of Contents](index.html) [Next](17-01.html)