< Free Open Study > |
8.3 Packages, Packages, PackagesThere are many regex packages for Java; the list that follows has a few words about those that I investigated while researching this book. (See this book's web page, regex.info/, for links). The table below gives a superficial overview of some of the differences among their flavors.
8.3.1 Why So Many "Perl5" Flavors?The list mentions "Perl-like" fairly often; the packages themselves advertise "Perl5 support." When version 5 of Perl was released in 1994 (see Section 3.1.1.7), it introduced a new level of regular-expression innovation that others, including Java regex developers, could well appreciate. Perl's regex flavor is powerful, and its adoption by a wide variety of packages and languages has made it somewhat of a de facto standard. However, of the many packages, programs, and languages that claim to be "Perl5 compliant," none truly are. Even Perl itself differs from version to version as new features are added and bugs are fixed. Some of the innovations new with early 5.x versions of Perl were non-capturing parentheses, lazy quantifiers, lookahead, inline mode modifiers like (?i) , and the /x free-spacing mode (all discussed in Chapter 3). Packages supporting only these features claim a "Perl5" flavor, but miss out on later innovations, such as lookbehind, atomic grouping, and conditionals. There are also times when a package doesn't limit itself to only "Perl5" enhancements. Sun's package, for example, supports possessive quantifiers, and both Sun and IBM support character class set operations. Pat offers an innovative way to do lookbehind, and a way to allow matching of simple arbitrarily nested constructs. 8.3.2 Lies, Damn Lies, and BenchmarksIt's probably a common twist on Sam Clemens' famous "lies, damn lies, and statistics" quote, but when I saw its use with "benchmarks" in a paper from Sun while doing research for this chapter, I knew it was an appropriate introduction for this section. In researching these seven packages, I've run literally thousands of benchmarks, but the only fact that's clearly emerged is that there are no clear conclusions. There are several things that cloud regex benchmarking with Java. First, there are language issues. Recall the benchmarking discussion from Chapter 6 (see Section 6.3.2), and the special issues that make benchmarking Java a slippery science at best (primarily, the effects of the Just-In-Time or Better-Late-Than-Never compiler). In doing these benchmarks, I've made sure to use a server VM that was "warmed up" for the benchmark (see "BLTN" Section 6.3.2), to show the truest results. Then there are regex issues. Due to the complex interactions of the myriad of optimizations like those discussed in Chapter 6, a seemingly inconsequential change while trying to test one feature might tickle the optimization of an unrelated featur e, anonymously skewing the results one way or the other. I did many (many!) very specific tests, usually approaching an issue from multiple directions, and so I believe I've been able to get meaningful results . . . but one never truly knows. 8.3.2.1 Warning: Benchmark results can cause drowsiness!Just to show how slippery this all can be, recall that I judged the two Jakarta packages (ORO and Regexp) to be roughly comparable in speed. Indeed, they finished equally in some of the many benchmarks I ran, but for the most part, one generally ran at least twice the speed of the other (sometimes 10x or 20x the speed). But which was "one" and which "the other" changed depending upon the test. For example, I targeted the speed of greedy and lazy quantifiers by applying ^.*: and ^.*?: to a very long string like '···xxx:x'. I expected the greedy one to be faster than the lazy one with this type of string, and indeed, it's that way for every package, program, and language I tested . . . except one. For whatever reason, Jakarta's Regexp's ^.*: performed 70% slower than its ^.*?: . I then applied the same expressions to a similarly long string, but this time one like 'x:xxx···' where the ':' is near the beginning. This should give the lazy quantifier an edge, and indeed, with Regexp, the expression with the lazy quantifier finished 670x faster than the greedy. To gain more insight, I applied ^[^:]*: to each string. This should be in the same ballpark, I thought, as the lazy version, but highly contingent upon certain optimizations that may or may not be included in the engine. With Regexp, it finished the test a bit slower than the lazy version, for both strings. Does the previous paragraph make your eyes glaze over a bit? Well, it discusses just six tests, and for only one regex package — we haven't even started to compar e these Regexp results against ORO or any of the other packages. When compar ed against ORO, it turns out that Regexp is about 10x slower with four of the tests, but about 20x faster with the other two! It's faster with ^.*?: and ^[^:]*: applied to the long string with ':' at the front, so it seems that Regexp does poorly (or ORO does well) when the engine must walk through a lot of string, and that the speeds are reversed when the match is found quickly. Are you eyes completely glazed over yet? Let's try the same set of six tests, but this time on short strings instead of very long ones. It turns out that Regexp is faster— three to ten times faster — than ORO for all of them. Okay, so what does this tell us? Perhaps that ORO has a lot of clunky overhead that overshadows the actual match time when the matches are found quickly. Or perhaps it means that Regexp is generally much faster, but has an inefficient mechanism for accessing the target string. Or perhaps it's something else altogether. I don't know. Another test involved an "exponential match" (see Section 6.1.4) on a short string, which tests the basic churning of an engine as it tracks and backtracks. These tests took a long time, yet Regexp tended to finish in half the time of ORO. There just seems to be no rhyme nor reason to the results. Such is often the case when benchmarking something as complex as a regex engine. 8.3.2.2 And the winner is . . .The mind-numbing statistics just discussed take into account only a small fraction of the many, varied tests I did. In looking at them all for Regexp and ORO, one package does not stand out as being faster overall. Rather, the good points and bad points seem to be distributed fairly evenly between the two, so I (perhaps somewhat arbitrarily) judge them to be about equal. Adding the benchmarks from the five other packages into the mix results in a lot of drowsiness for your author, and no obviously clear winner, but overall, Sun's package seems to be the fastest, followed closely by IBM's. Following in a group somewhat behind are Pat, Jregex, Regexp, and ORO. The GNU package is clearly the slowest. The overall difference between Sun and IBM is not so obviously clear that another equally comprehensive benchmark suite wouldn't show the opposite order if the suite happened to be tweaked slightly differently than mine. Or, for that matter, it's entirely possible that someone looking at all my benchmark data would reach a different conclusion. And, of course, the results could change drastically with the next release of any of the packages or virtual machines (and may well have, by the time you read this). It's a slippery science. In general, Sun did most things very well, but it's missing a few key optimizations, and some constructs (such as character classes) are much slower than one would expect. Over time, these will likely be addressed by Sun (and in fact, the slowness of character classes is slated to be fixed in Java 1.4.2). The source code is available if you'd like to hack on it as well; I'm sure Sun would appreciate ideas and patches that improve it. 8.3.3 RecommendationsThere are many reasons one might choose one package over another, but Sun's java.util.regex package—with its high quality, speed, good Unicode support, advanced features, and future ubiquity—is a good recommendation. It comes integrated as part of Java 1.4: String.matches(), for example, checks to see whether the string can be completely matched by a given regex. java.util.regex's strengths lie in its core engine, but it doesn't have a good set of "convenience functions," a layer that hides much of the drudgery of bit-shuffling behind the scenes. ORO, on the other hand, while its core engine isn't as strong, does have a strong support layer. It provides a very convenient set of functions for casual use, as well as the core interface for specialized needs. ORO is designed to allow multiple regex core engines to be plugged in, so the combination of java.util.regex with ORO sounds very appealing. I've talked to the ORO developer, and it seems likely that this will happen, so the rest of this chapter looks at Sun's java.util.regex and ORO's interface. |
< Free Open Study > |