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

85 lines
8.8 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: Boyer-Moore String Searching</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=14//-->
<!--PAGES=260-263//-->
<!--UNASSIGNED1//-->
<!--UNASSIGNED2//--></HEAD><BODY LINK=#0000FF ALINK=#000099 VLINK=#0000FF BGCOLOR=#FFFFFF>
<CENTER>
<TABLE BORDER>
<TR>
<TD><A HREF="13-04.html">Previous</A></TD>
<TD><A HREF="index.html">Table of Contents</A></TD>
<TD><A HREF="14-02.html">Next</A></TD>
</TR>
</TABLE>
</CENTER>
<P><BR></P>
<H2><A NAME="Heading1"></A><FONT COLOR="#000077">Chapter 14<BR>Boyer-Moore String Searching
</FONT></H2>
<H3><A NAME="Heading2"></A><FONT COLOR="#000077">Optimizing a Pretty Optimum Search Algorithm</FONT></H3>
<P>When you seem to be stumped, stop for a minute and <I>think.</I> All the information you need may be right in front of your nose if you just look at things a little differently. Here&#146;s a case in point:</P>
<P>When I was in college, I used to stay around campus for the summer. Oh, I&#146;d take a course or two, but mostly it was an excuse to hang out and have fun. In that spirit, my girlfriend, Adrian (<I>not</I> my future wife, partly for reasons that will soon become apparent), bussed in to spend a week, sharing a less-than-elegant $150 per month apartment with me and, by necessity, my roommate.</P>
<P>Our apartment was pretty much standard issue for two male college students; maybe even a cut above. The dishes were usually washed, there was generally food in the refrigerator, and nothing larger than a small dog had taken up permanent residence in the bathroom. However, there was one sticking point (literally): the kitchen floor. This floor&#151;standard tile, with a nice pattern of black lines on an off-white background (or so we thought)&#151;had never been cleaned. By which I mean that I know for a certainty that <I>we</I> had never cleaned it, but I suspect that it had in fact not been cleaned since the Late Jurassic, or possibly earlier. Our feet tended to stick to it; had the apartment suddenly turned upside-down, I think we&#146;d all have been hanging from the ceiling.</P>
<P>One day, my roommate and I returned from a pick-up basketball game. Adrian, having been left to her own devices for a couple of hours, had apparently kept herself busy. &#147;Notice anything?&#148; she asked, with an edge to her voice that suggested we had damned well better.</P>
<P>&#147;Uh, you cooked dinner?&#148; I guessed. &#147;Washed the dishes? Had your hair done?&#148; My roommate was equally without a clue.</P>
<P>She stamped her foot (really; the only time I&#146;ve ever seen it happen), and said, &#147;No, you jerks! The kitchen floor! Look at the floor! I cleaned it!&#148;</P>
<P>The floor really did look amazing. It was actually all white; the black lines had been grooves filled with dirt. We assured her that it looked terrific, it just wasn&#146;t that obvious until you knew to look for it; anyone would tell you that it wasn&#146;t the kind of thing that jumped out at you, but it really was great, no kidding. We had almost smoothed things over, when a friend walked in, looked around with a start, and said, &#147;Hey! Did you guys put in a new floor?&#148;</P>
<P>As I said, sometimes everything you need to know is right in front of your nose. Which brings us to Boyer-Moore string searching.</P>
<H3><A NAME="Heading3"></A><FONT COLOR="#000077">String Searching Refresher</FONT></H3>
<P>I&#146;ve discussed string searching earlier in this book, in Chapters 5 and 9. You may want to refer back to these chapters for some background on string searching in general. I&#146;m also going to use some of the code from that chapter as part of this chapter&#146;s test suite. For further information, you may want to refer to the discussion of string searching in the excellent <I>Algorithms in C,</I> by Robert Sedgewick (Addison-Wesley), which served as the primary reference for this chapter. (If you look at Sedgewick, be aware that in the Boyer-Moore listing on page 288, there is a mistake: &#147;j &gt 0&#148; in the <B>for</B> loop should be &#147;j &gt= 0,&#148; unless I&#146;m missing something.)</P>
<P>String searching is the simple matter of finding the first occurrence of a particular sequence of bytes (the pattern) within another sequence of bytes (the buffer). The obvious, brute-force approach is to try every possible match location, starting at the beginning of the buffer and advancing one position after each mismatch, until either a match is found or the buffer is exhausted. There&#146;s even a nifty string instruction, <B>REPZ CMPS,</B> that&#146;s perfect for comparing the pattern to the contents of the buffer at each location. What could be simpler?</P>
<P>We have some important information that we&#146;re not yet using, though. Typically, the buffer will contain a wide variety of bytes. Let&#146;s assume that the buffer contains text, in which case there will be dozens of different characters; and although the distribution of characters won&#146;t usually be even, neither will any one character constitute half the buffer, or anything close. A reasonable conclusion is that the first character of the pattern will rarely match the first character of the buffer location currently being checked. This allows us to use the speedy <B>REPNZ SCASB</B> to whiz through the buffer, eliminating most potential match locations with single repetitions of <B>SCASB.</B> Only when that first character does (infrequently) match must we drop back to the slower <B>REPZ CMPS</B> approach.</P>
<P>It&#146;s important to understand that we&#146;re assuming that the buffer is typical text. That&#146;s what I meant at the outset, when I said that the information you need may be under your nose.</P>
<TABLE WIDTH="100%"><TD ALIGN="LEFT" VALIGN="TOP" WIDTH="5%"><IMG SRC="images/14-01i.jpg"><TD ALIGN="LEFT" VALIGN="TOP" WIDTH="95%"><SMALL><I>Formally, you don&#146;t know a blessed thing about the search buffer, but experience, common sense, and your knowledge of the application give you a great deal of useful, if somewhat imprecise, information.</I></SMALL>
</TABLE>
<P>If the buffer contains the letter &#145;A&#146; repeated 1,000 times, followed by the letter &#145;B,&#146; then the <B>REPNZ SCASB/REPZ CMPS</B> approach will be much slower than the brute-force <B>REPZ CMPS</B> approach when searching for the pattern &#147;AB,&#148; because <B>REPNZ SCASB</B> would match at every buffer location. You could construct a horrendous worst-case scenario for almost any good optimization; the key is understanding the usual conditions under which your code will work.</P>
<P>As discussed in Chapter 9, we also know that certain characters have lower probabilities of matching than others. In a normal buffer, &#145;T&#146; will match far more often than &#145;X.&#146; Therefore, if we use <B>REPNZ SCASB</B> to scan for the least common letter in the search string, rather than the first letter, we&#146;ll greatly decrease the number of times we have to drop back to <B>REPZ CMPS,</B> and the search time will become very close to the time it takes <B>REPNZ SCASB</B> to go from the start of the buffer to the match location. If the distance to the first match is N bytes, the least-common <B>REPNZ SCASB</B> approach will take about as long as N repetitions of <B>REPNZ SCASB.</B></P>
<P>At this point, we&#146;re pretty much searching at the speed of <B>REPNZ SCASB.</B> On the x86, there simply is no faster way to test each character in turn. In order to get any faster, we&#146;d have to check fewer characters&#151;but we can&#146;t do that and still be sure of finding all matches. Can we?</P>
<P>Actually, yes, we can.</P><P><BR></P>
<CENTER>
<TABLE BORDER>
<TR>
<TD><A HREF="13-04.html">Previous</A></TD>
<TD><A HREF="index.html">Table of Contents</A></TD>
<TD><A HREF="14-02.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 -->