abrash-black-book/08-03.html
2013-12-30 12:21:49 +11:00

227 lines
9.3 KiB
HTML

<HTML>
<HEAD>
<META name=vsisbn content="1576101746">
<META name=vstitle content="Michael Abrash's Graphics Programming Black Book, Special Edition">
<META name=vsauthor content="Michael Abrash">
<META name=vspublisher content="The Coriolis Group">
<META name=vspubdate content="07/01/97">
<META name=vscategory content="Web and Software Development: Game Development,Web and Software Development: Graphics and Multimedia Development">
<TITLE>Michael Abrash's Graphics Programming Black Book Special Edition: Speeding Up C with Assembly Language</TITLE>
<!-- HEADER -->
<!-- Empty Reference Subhead -->
<!--ISBN=1576101746//-->
<!--TITLE=Michael Abrash's Graphics Programming Black Book Special Edition//-->
<!--AUTHOR=Michael Abrash//-->
<!--PUBLISHER=The Coriolis Group, Inc.//-->
<!--CHAPTER=08//-->
<!--PAGES=156-160//-->
<!--UNASSIGNED1//-->
<!--UNASSIGNED2//--></HEAD><BODY LINK=#0000FF ALINK=#000099 VLINK=#0000FF BGCOLOR=#FFFFFF>
<CENTER>
<TABLE BORDER>
<TR>
<TD><A HREF="08-02.html">Previous</A></TD>
<TD><A HREF="index.html">Table of Contents</A></TD>
<TD><A HREF="08-04.html">Next</A></TD>
</TR>
</TABLE>
</CENTER>
<P><BR></P>
<P><B>LISTING 8.1 L8-1.C</B></P>
<!-- CODE //-->
<PRE>
/* Program to search an array spanning a linked list of variable-
sized blocks, for all entries with a specified ID number,
and return the average of the values of all such entries. Each of
the variable-sized blocks may contain any number of data entries,
stored as an array of structures within the block. */
#include &ltstdio.h&gt
#ifdef __TURBOC__
#include &ltalloc.h&gt
#else
#include &ltmalloc.h&gt
#endif
void main(void);
void exit(int);
unsigned int FindIDAverage(unsigned int, struct BlockHeader *);
/* Structure that starts each variable-sized block */
struct BlockHeader {
struct BlockHeader *NextBlock; /* Pointer to next block, or NULL
if this is the last block in the
linked list */
unsigned int BlockCount; /* The number of DataElement entries
in this variable-sized block */
};
/* Structure that contains one element of the array we&#146;ll search */
struct DataElement {
unsigned int ID; /* ID # for array entry */
unsigned int Value; /* Value of array entry */
};
void main(void) {
int i,j;
unsigned int IDToFind;
struct BlockHeader *BaseArrayBlockPointer,*WorkingBlockPointer;
struct DataElement *WorkingDataPointer;
struct BlockHeader **LastBlockPointer;
printf(&#148;ID # for which to find average: &#147;);
scanf(&#148;%d&#148;,&ampIDToFind);
/* Build an array across 5 blocks, for testing */
/* Anchor the linked list to BaseArrayBlockPointer */
LastBlockPointer = &ampBaseArrayBlockPointer;
/* Create 5 blocks of varying sizes */
for (i = 1; i &lt 6; i&#43;&#43;) {
/* Try to get memory for the next block */
if ((WorkingBlockPointer =
(struct BlockHeader *) malloc(sizeof(struct BlockHeader) &#43;
sizeof(struct DataElement) * i * 10)) == NULL) {
exit(1);
}
/* Set the # of data elements in this block */
WorkingBlockPointer-&gtBlockCount = i * 10;
/* Link the new block into the chain */
*LastBlockPointer = WorkingBlockPointer;
/* Point to the first data field */
WorkingDataPointer =
(struct DataElement *) ((char *)WorkingBlockPointer &#43;
sizeof(struct BlockHeader));
/* Fill the data fields with ID numbers and values */
for (j = 0; j &lt (i * 10); j&#43;&#43;, WorkingDataPointer&#43;&#43;) {
WorkingDataPointer-&gtID = j;
WorkingDataPointer-&gtValue = i * 1000 &#43; j;
}
/* Remember where to set link from this block to the next */
LastBlockPointer = &ampWorkingBlockPointer-&gtNextBlock;
}
/* Set the last block&#146;s &#147;next block&#148; pointer to NULL to indicate
that there are no more blocks */
WorkingBlockPointer-&gtNextBlock = NULL;
printf(&#148;Average of all elements with ID %d: %u\n&#148;,
IDToFind, FindIDAverage(IDToFind, BaseArrayBlockPointer));
exit(0);
}
/* Searches through the array of DataElement entries spanning the
linked list of variable-sized blocks, starting with the block
pointed to by BlockPointer, for all entries with IDs matching
SearchedForID, and returns the average value of those entries. If
no matches are found, zero is returned */
unsigned int FindIDAverage(unsigned int SearchedForID,
struct BlockHeader *BlockPointer)
{
struct DataElement *DataPointer;
unsigned int IDMatchSum;
unsigned int IDMatchCount;
unsigned int WorkingBlockCount;
IDMatchCount = IDMatchSum = 0;
/* Search through all the linked blocks until the last block
(marked with a NULL pointer to the next block) has been
searched */
do {
/* Point to the first DataElement entry within this block */
DataPointer =
(struct DataElement *) ((char *)BlockPointer &#43;
sizeof(struct BlockHeader));
/* Search all the DataElement entries within this block
and accumulate data from all that match the desired ID */
for (WorkingBlockCount=0;
WorkingBlockCount&ltBlockPointer-&gtBlockCount;
WorkingBlockCount&#43;&#43;, DataPointer&#43;&#43;) {
/* If the ID matches, add in the value and increment the
match counter */
if (DataPointer-&gtID == SearchedForID) {
IDMatchCount&#43;&#43;;
IDMatchSum &#43;= DataPointer-&gtValue;
}
}
/* Point to the next block, and continue as long as that pointer
isn&#146;t NULL */
} while ((BlockPointer = BlockPointer-&gtNextBlock) != NULL);
/* Calculate the average of all matches */
if (IDMatchCount == 0)
return(0); /* Avoid division by 0 */
else
return(IDMatchSum / IDMatchCount);
}
</PRE>
<!-- END CODE //-->
<P>The main body of Listing 8.1 constructs a linked list of memory blocks of various sizes and stores an array of structures across those blocks, as shown in Figure 8.2. The function <B>FindIDAverage</B> in Listing 8.1 searches through that array for all matches to a specified ID number and returns the average value of all such matches. <B>FindIDAverage</B> contains two nested loops, the outer one repeating once for each linked block and the inner one repeating once for each array element in each block. The inner loop&#151;the critical one&#151;is compact, containing only four statements, and should lend itself rather well to compiler optimization.</P>
<P><A NAME="Fig2"><!-- </A><A HREF="javascript:displayWindow('images/08-02.jpg',413,265 )"> --><IMG SRC="images/08-02.jpg"><BR><!-- </A>
<BR><A HREF="javascript:displayWindow('images/08-02.jpg',413,265)"> --><FONT COLOR="#000077"><B>Figure 8.2</B></FONT></A>&nbsp;&nbsp;<I>Linked array storage format (version 1).</I>
</P>
<P>As it happens, Microsoft C/C<SMALL>&#43;&#43;</SMALL> does optimize the inner loop of <B>FindIDAverage</B> nicely. Listing 8.2 shows the code Microsoft C/C<SMALL>&#43;&#43;</SMALL> generates for the inner loop, consisting of a mere seven assembly language instructions inside the loop. The compiler is smart enough to convert the loop index variable, which counts up but is used for nothing but counting loops, into a count-down variable so that the <B>LOOP</B> instruction can be used.</P>
<P><B>LISTING 8.2 L8-2.COD</B></P>
<!-- CODE //-->
<PRE>
; Code generated by Microsoft C for inner loop of FindIDAverage.
;|*** for (WorkingBlockCount=0;
;|*** WorkingBlockCount&ltBlockPointer-&gtBlockCount;
;|*** WorkingBlockCount&#43;&#43;, DataPointer&#43;&#43;) {
mov WORD PTR [bp-6],0 ;WorkingBlockCount
mov bx,WORD PTR [bp&#43;6] ;BlockPointer
cmp WORD PTR [bx&#43;2],0
je $FB264
mov cx,WORD PTR [bx&#43;2]
add WORD PTR [bp-6],cx ;WorkingBlockCount
mov di,WORD PTR [bp-2] ;IDMatchSum
mov dx,WORD PTR [bp-4] ;IDMatchCount
$L20004:
;|*** if (DataPointer-&gtID == SearchedForID) {
mov ax,WORD PTR [si]
cmp WORD PTR [bp&#43;4],ax ;SearchedForID
jne $I265
;|*** IDMatchCount&#43;&#43;;
inc dx
;|*** IDMatchSum &#43;= DataPointer-&gtValue;
add di,WORD PTR [si&#43;2]
;|*** }
;|*** }
$I265:
add si,4
loop $L20004
mov WORD PTR [bp-2],di ;IDMatchSum
mov WORD PTR [bp-4],dx ;IDMatchCount
$FB264:
</PRE>
<!-- END CODE //-->
<P><BR></P>
<CENTER>
<TABLE BORDER>
<TR>
<TD><A HREF="08-02.html">Previous</A></TD>
<TD><A HREF="index.html">Table of Contents</A></TD>
<TD><A HREF="08-04.html">Next</A></TD>
</TR>
</TABLE>
</CENTER>
<hr width="90%" size="1" noshade>
<div align="center">
<font face="Verdana,sans-serif" size="1">Graphics Programming Black Book &copy; 2001 Michael Abrash</font>
</div>
<!-- all of the reference materials (books) have the footer and subfoot reveresed -->
<!-- reference_subfoot = footer -->
<!-- reference_footer = subfoot -->
<!-- BEGIN SUB FOOTER -->
</BODY>
</HTML>
<!-- END FOOTER -->