Rust benchmark: cached lowercase keys beat iterator sort, unicase close
A Rust developer benchmarked three ways to sort a Vec
Key facts
- The benchmark compares three case insensitive sort strategies for Vec
in Rust: sort_by_cached_key with .to_lowercase(), sort_by comparing char::to_lowercase() iterators directly, and sort_by using the unicase crate, across list sizes from 1 to 10000, using divan on an M2-MAX MacBook Pro. - At 10000 elements, median sort times were 864.8 microseconds for the cached lowercase key, 1.757 milliseconds for unicase, and 5.572 milliseconds for the plain iterator comparison, making the iterator approach about 6.4 times slower than the cached key.
- At small sizes the ranking flips: at 5 elements, unicase (91.86 ns) and the plain iterator comparison (137.4 ns) both beat the cached key approach (215.5 ns), with unicase about 2.3 times faster than caching the key.
- The author's takeaway is that sort_by_cached_key is worth it unless sorting only a single element, and that character by character lowercase conversion is far slower than expected, which cancels out the benefit of not allocating.
- Rust's Iterator type has a cmp method but does not implement Ord because calling cmp exhausts the iterator, which is why the iterator based comparison has to interleave lowercase conversion with sort_by rather than use a cached-key style call.
Why it matters
Case insensitive sorting is a routine task, and it is easy to assume that avoiding an allocation (comparing on the fly) will always beat allocating a lowercased copy of every string up front. This benchmark shows that assumption is wrong for anything beyond a handful of elements: iterating over Unicode characters to lowercase them on every comparison scales badly, because a sort makes far more than one comparison per element as the list grows.
Who it affects
Rust developers who sort user-facing text such as names, filenames or tags and need the sort to ignore letter case. It also matters to anyone choosing between rolling a manual Unicode-aware comparison and pulling in a small crate like unicase for the same job.
How to use it
For lists past a few dozen items, sort_by_cached_key with .to_lowercase() is the safer default: it allocates one lowercased String per element up front but avoids repeating the conversion on every comparison. The unicase crate is a close, sometimes faster, alternative that needs no manual key extraction and does not require the cached-key workaround for Iterator's lack of Ord. A plain iterator based comparison written by hand is only competitive at very small list sizes, around five elements or fewer in this benchmark.
How solid is it
This is a single informal benchmark run on one machine, an M2-MAX MacBook Pro, using the divan benchmarking crate with 100 samples per case and synthetic English names generated by the fake crate, across sizes from 1 to 10000. It is a real, reproducible measurement with code included, but it reflects one hardware target and one kind of input string rather than a broad, peer-reviewed study.
Risks and caveats
The results come from one machine and one input shape (short English names); behavior could differ on other hardware, with longer strings, or with non-Latin scripts where case folding is more complex. The author offers no explanation for why unicase often beats the hand-rolled cached-key approach despite a more complex comparison, so that finding is reported without a settled cause. No version numbers are given for the divan, fake, or unicase crates used, and no date is given for when the benchmark was run.
“unless you only have one element (which is the trivial case), sort_by_cached_key is worth it”
— the blog's author