@@ -0,0 +1,93 @@
1+In my
2+[first of this pair of articles](http://www.serpentine.com/blog/2010/03/03/whats-in-a-parsing-library-1/),
3+I laid out some of the qualities I've been looking for in a parsing
4+library.
5+
6+Before I dive back into detail, I want to show off some numbers. The
7+new Attoparsec code is _fast_.
8+
9+
10+
11+What did I benchmark? I captured some real HTTP GET requests from a
12+live public web server, averaging 431 bytes per request. I chucked
13+them into a file, and measured the time needed to parse the entire
14+contents of the file with the following libraries:
15+
16+* Ryan Dahl's [http-parser](http://github.com/ry/http-parser) library,
17+ which is 1,672 lines of hand-rolled C craziness. Its heritage seems
18+ to be closely based on the Ragel-generated parser used by Mongrel.
19+ This library is a fair approximation to about as fast as you can
20+ get, since it's been tuned for just one purpose. I wrote a small,
21+ but reasonably realistic,
22+ [driver program](http://bitbucket.org/bos/attoparsec/src/tip/examples/rfc2616.c)
23+ to wire it up to file-based data, adding another 210 lines of code.
24+
25+* An Attoparsec-based
26+ [HTTP request parser](http://bitbucket.org/bos/attoparsec/src/tip/examples/RFC2616.hs),
27+ 54 lines long, with about 30 lines of
28+ [driver program](http://bitbucket.org/bos/attoparsec/src/tip/examples/TestRFC2616.hs).
29+ (Attoparsec itself is about 900 lines of code.)
30+
31+* Several
32+ [Parsec-3-based parsers](http://bitbucket.org/bos/attoparsec/src/tip/examples/Parsec_RFC2616.hs),
33+ which are almost identical in length to the Attoparsec-based
34+ version.
35+
36+The Parsec 3 parsers come in three varieties:
37+
38+* The fastest uses a patch that Antoine Latter wrote to switch Parsec
39+ 3's internal machinery over to using continuation passing style
40+ (CPS). This parser uses `ByteString` for input, and reads the
41+ entire 18.8MB file in one chunk.
42+
43+* Next is the same parser, using lazy `ByteString` I/O to read the
44+ file in 64KB chunks. This costs about 50% in performance, but is
45+ almost mandatory to maintain a sensible footprint on large inputs.
46+
47+* In last place is the official version of Parsec 3, reading the input
48+ in one chunk. (Reading lazily still costs an additional 50%, but I
49+ didn't want to further clutter the chart with more big numbers.)
50+
51+What's interesting to me is that the tiny Attoparsec-based parser,
52+which is more or less a transliteration of the relevant parts of
53+[RFC 2616](http://www.w3.org/Protocols/rfc2616/rfc2616.html), is so
54+fast.
55+
56+I went back and remeasured performance of the Attoparsec and C parsers
57+on a larger data set (295,568 URLs), and got these numbers:
58+
59+* Attoparsec: 2.889 seconds, or 102,308 requests/second
60+
61+* C: 1.614 seconds, or 183,128 requests/second
62+
63+That clocks the Attoparsec-based parser at about 56% the speed of the
64+C parser. Not bad, given that it's about 3.2% the number of lines of
65+code!
66+
67+Of course there are tradeoffs involved here.
68+
69+* Parsec 3 emits much more friendly error messages, and can handle
70+ many different input types. Attoparsec, being aimed at
71+ plumbing-oriented network protocols, considers friendly error
72+ messages to not be worth the effort, and is specialised to the
73+ arrays of bytes you get straight off the network.
74+
75+* Parsec 3 requires all of its input to be available when the parser
76+ is run (either in one large chunk or via lazy I/O). If Attoparsec
77+ has insufficient data to return a complete result, it hands back a
78+ continuation that you provide extra data to. This eliminates the
79+ need for lazy I/O and any additional buffering, and makes for a
80+ beautiful, pure API that doesn't care what its input source is.
81+
82+The memory footprint of the Attoparsec-based parser is small: it will
83+run in 568KB of heap on my 64-bit laptop. The smallest heap size that
84+the Parsec 3 parser can survive in isn't all that much larger: with
85+lazily read input, it will run in a 750KB heap.
86+
87+Overall, this is yet another instance where a little careful attention
88+to performance yields very exciting results. Personally, I'd be quite
89+happy to trade a 97% reduction in code size for such a small
90+performance hit, especially given the clarity, ease of use, and
91+flexibility of the resulting code. (The `http_parser` API is frankly
92+not so much fun to use, even though I completely understand the
93+motivation behind it.)