Skip to content
shellhacks.dev
Performance

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.

2 min read
Summarize in AI

A grep with a character class takes seconds on a file that a plain string search reads in a fraction of one.

Shell
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+$'
Text
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 s

In 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.

Share

More snippets

Performance

Trace file opens of a command

Find which files a command opens with strace -e trace=openat, and add -e status=failed to see the config files it looked for and did not find.

2 min read
Performance

Count process spawns in a script

Count how many processes a shell script starts with strace -f -c -e trace=execve,clone. A loop that calls date five times runs six execs, and a builtin runs one.

1 min read
Performance

Find syscalls that take time

Show how long each system call takes with strace -T. The figure in angle brackets is the time spent inside the call, so a slow write or sleep stands out.

1 min read

Get new posts by email

One email when a new post is published. No spam.

Unsubscribe in one click. No spam, ever.