2000x performance win
dtrace.org
dtrace.org
The PoSix locale system is a nightmare, but the implementation in GNU coreutils is even worse, when it is not insanely slow it is plain broken.
I wasted days tracking down a bug when we moved a script from FreeBSD to a linux sytem that had an UTF-8 locale, even for plain ASCII input GNU awk could match lines erratically if the locale was not set back to C! I'm sure this bug has been fixed by now, but is by far not the only time I have found serious flaws with UTF-8 support in GNU tools, it seems this days the only way to be safe is to set the locale to C at the top of all your scripts, which is quite sad.
This is also required because due to POSIX locale insanity the actual behavior of tools as basic as ls(1), and more fundamentally regexps matching changes based on the locale, making scripts that don't set their locale at the top unreliable and nonportable.
Or using a toolkit with decent UTF-8 support, like the from Plan 9 that one can use on *nix systems via Plan 9 from User Space ( http://plan9.us ) or 9base ( http://tools.suckless.org/9base ), if you use those also your scripts will be much more portable, as there are all kinds of incompatibilities among for example awk implementations.
Plan9 regexps are very easy to reason about, more explicit and don't look as line noise as much.
$ echo HI | LANG=en_US.utf8 grep '^[a-z]'
HI
$ echo HI | LANG=C grep '^[a-z]'
$
apparently en_{GB,US}.utf8 orders a-z like aAbBcC..zZ. $ echo ZI | LANG=en_US.utf8 grep '^[a-z]'
$ $ echo HI | LANG=C grep '^[a-z]'
$ echo HI | LANG=en_US.utf8 grep '^[a-z]'
$
How come? ; grep --version
GNU grep 2.6.3
; echo A | LANG=en_US.utf8 grep '[a-z]'
A $ sort <<EOF
> Aa
> aa
> Ab
> ab
> EOF
aa
Aa
ab
Ab
I was going crazy because I was getting different results in OSX and Ubuntu. Setting the LANG to POSIX fixed it. $ sort --version
sort (GNU coreutils) 8.14
For what it's worth, this gets me the same results under LANG=C and LANG=en_US.utf8Actually, GNU grep as of last year was much faster than BSD grep, as long as the C locale is used:
http://lists.freebsd.org/pipermail/freebsd-current/2010-Augu...
The reason why using UTF8 makes it much slower is that dealing with Unicode correctly is a very expensive business. But pretending that languages other than english do not exist is not an answer.
> dealing with Unicode correctly is a very expensive business
is plain FUD.
UTF-8 has zero overhead in this case (not near zero, but actual zero overhead). Grep doesn't even need to be aware of UTF-8. It doesn't need to be aware where a rune starts and ends in the byte stream. UTF-8 is designed in such a way that matching a regexp only requires byte comparisons, exactly as for ASCII.
>> dealing with Unicode correctly is a very expensive business
> is plain FUD.
If you think this is true you don't know unicode enough. I don't know how plan9 grep is implemented but if it does it with near zero overhead then it's not actually supporting Unicode, but only with a small subset of Unicode that they like. I also probably agree that that's the best part of Unicode and the rest of Unicode could burn in a fire but it's still not Unicode.
Yes, well, that is sort of a given for any statement about unicode (including this one).
If you just deal with it on a byte by byte basis, you're not actually Unicode compatible. It might be fast, but it's not Correct.
edit: a discussion on this subject from a few months ago, where I was corrected on that point: http://news.ycombinator.com/item?id=2860932
To have any usable grep with applicability beyond programming languages, English-language log-files, etc. (i.e. massive Anglocentrism), the normalizing would need to be built-in. Sketched:
#!/usr/bin/bash
regex="$1"; shift
unicode-normalize-to-utf8 "$@" | byte-grep "$(utf8-byteify-regex "$regex")"Actually, why is SmartOS, "the Completely Modern Operating System", shipping with a five year old grep?
Release notes for 2.6:
"This release fixes an unexpectedly large number of flaws, from outright bugs (surprisingly many, considering this is "grep") to some occasionally debilitating performance problems."
UTF-8 regular expression matching shouldn't be different from ASCII at all, as far as I can tell. In UTF-8, every byte by itself can be identified as a start byte or trail byte, so if you want to match a regular expression, you don't even have to care about UTF-8 in any way. Any legal character in the regexp you want to look for can only match at a legal character start in the haystack.
You are completely right, matching regular expressions with UTF-8 text is the same as with ASCII text, one of the reasons why UTF-8 is so good.
The issue is completely unrelated to the fact that matching regexps in UTF-8 text is the same as matching regexp in ASCII text. The regular expression tool doesn't even need to care that the text is UTF-8. It's just byte comparisons, the tool doesn't even need to be aware of rune boundaries.
P.S.: and sometimes there are a lot of problems with lower and upper-case letters http://www.gnu.org/s/grep/devel.html
(I'm particularly thinking of Boyer-Moore -- http://en.wikipedia.org/wiki/Boyer%E2%80%93Moore_string_sear... -- but I'm sure there are other examples as well.)
If it's just doing byte comparisons, then the tool isn't properly implementing http://en.wikipedia.org/wiki/Unicode_equivalence, unless it can assume that all input text was already normalized.
Not really. First the '.' operator needs to work differently. This can be still done fast, but if you actually want to claim unicode support you should also consider:
- Unicode collation orders
- Unicode string equivalence algorithms
- Unicode normalization algorithms
This will also take care of all the unicode weirdnesses that no one actually uses or cares about but you still must implement to claim compatibility like: presentational forms, combining diacritics, ligatures, double sized characters and other odd stuff.
> The 'C' locale is defined as the "default" locale
> for applications, meaning that their strings are
> displayed as written in the initial code (without
> passing through a translation lookup).
>
> There is nothing special about the C locale, except
> that it will always exist and applies no string
> replacements.
-Malcolm Tredinnick
http://mailman.linuxchix.org/pipermail/techtalk/2002-Novembe... > If the locale value is "C" or "POSIX", the POSIX locale is used
- The Single UNIX ® Specification, Version 2
http://pubs.opengroup.org/onlinepubs/7908799/xbd/envvar.htmlHence even perfect versions of grep will slower by arbitrarily large factors, depending on the input.
So while there may be problems, expecting no difference or no significant difference between encodings is not correct either
That you get UTF-8 input and produce UTF-8 output doesn't imply you are better off using UTF-8 for processing. Translating UTF-8 to fixed-width UTF-32 and back is of linear complexity and takes small, fixed amount of memory. The only trade-off is when processing /very/ long lines -- up to four time more memory would be used for buffer.
As mentioned in other posts, Unicode requires normalization of certain character combinations into other characters, so you'll be processing all input characters anyway. Just prefix an extra step to it, not even a separate loop.
And so you can do Boyer-Moore with Unicode at very little extra cost :-)
Some text-intensive programs of Plan 9, including grep, use internally fixed-widht format called `Rune' for unicode, exactly for reasons of performance. UTF-8 input is translated into strings of Runes for processing and translated back for output.
I'm not denying that grep was buggy (there's a link to grep's bug tracker to a bug that was closed more than a year ago), but I'm surprised at the magnitude of the slowdown.
For an old, unpatched GNU grep a 2000x slowdown is quite believable.
"This release fixes an unexpectedly large number of flaws, from outright bugs (surprisingly many, considering this is "grep") to some occasionally debilitating performance problems."
Current version of grep is 2.10.
Any risks with just updating to latest version of grep instead of using the LANG=C hack?
I only get a 2x improvement when switching LANG on Redhat linux on EC2:
% export LANG=C % time grep done nohup.out | wc -l 152929
real 0m0.343s user 0m0.233s sys 0m0.112s
% export LANG=en_US.UTF-8 % time grep done nohup.out | wc -l 152931
real 0m0.771s user 0m0.673s sys 0m0.100s
% grep --version GNU grep 2.6.3
Author is using grep 2.5.3, I am using 2.6.3, so not testing the same thing.