Speed up grep with LC_ALL C
Run grep with LC_ALL=C for speed on patterns with character classes or ranges. The speed-up is large on glibc, and matches change for non-ASCII text.
A grep with a character class takes seconds on a file that a plain string search reads in a fraction of one.
yes 'some line with é ünïcode and text' | head -n 3000000 > uni.txt
t() { local loc=$1 s e n; shift; s=$EPOCHREALTIME; n=$(LC_ALL=$loc grep -c "$@" uni.txt); e=$EPOCHREALTIME; printf '%-34s %8d %.2f s\n' "$loc $*" "$n" "$(awk -v a="$s" -v b="$e" 'BEGIN{print b-a}')"; }
t en_US.UTF-8 -e '[a-z]*ext$'
t C -e '[a-z]*ext$'
t en_US.UTF-8 -E '^(\w+ ){6}\w+$'
t C -E '^(\w+ ){6}\w+$'en_US.UTF-8 -e [a-z]*ext$ 3000000 3.54 s
C -e [a-z]*ext$ 3000000 0.31 s
en_US.UTF-8 -E ^(\w+ ){6}\w+$ 3000000 12.33 s
C -E ^(\w+ ){6}\w+$ 0 0.18 sIn a multibyte locale grep has to decode characters and apply locale rules for ranges, classes and the word class, and on glibc that slows down the patterns that need it. LC_ALL=C makes every byte a character, which is faster, and the first pair of lines shows the same count in a fraction of the time. The last pair shows the cost: the é is two bytes and not a word character in the C locale, so a line with it no longer matches and the answer changes. Locale handling is documented in the GNU grep manual.
Use LC_ALL=C only when the data is ASCII or when byte semantics are what you want, such as in logs. A plain string search showed no gain here, and the C.UTF-8 locale was as slow as en_US.UTF-8 on the class pattern. The numbers are from grep 3.8 on one 111 MB file with one run each. Set the variable for the one command, not the whole script, so sorting and other tools keep their order.