> On the other hand, you may still get away with writing more performant C code since C allows the developer to play fast and loose with the rules.
I have some thoughts on how Rust performance compares with C!
I spent yesterday optimizing a sort routine in Rust. I need to sort records where the key length is known at compile-time, and the payload length is different every time sort is called. I expect the average sort call to involve at least 250 million rows and over 400 columns. Speed matters.
Here's what I've learned so far, about how Rust versus C question:
- Rust has generics with monomorphization. This makes it easy to compile two different versions of the sort routine to run on different length keys.
- The very fastest version of my recursive base case, a 20-item insertion sort, currently uses unsafe code. This allows me to eliminate a couple of bounds checks that LLVM isn't eliminating automatically. I may figure out how to do this with safe code before shipping. (In 5+ years of production Rust, I've never needed unsafe for performance. This may be the first time!)
- Performance often comes down to cache locality. Both C and Rust give me the fine-grained memory layout control that's essential. Also, this means that "detached key" sorts are almost always a bad choice, even when the alternative is repeatly moving large record payloads around.
- The Rust "criterion" benchmarking library makes it super-easy to run valid performance tests.
- The Rust "proptest" allows me to generate large amounts of test data and verify properties like "reckless_sort always produces output matching std sort."
- "cargo fuzz" will be useful when I try to break the finished code. Seriously, it's a super-nice fuzzer workflow.
- Rust's standard quicksort contains all sorts of funky optimizations: It detects semi-sorted arrays, it breaks patterns, it falls back to merge sort when things go wrong. It even marks "cold" paths for LLVM to improve instruction cache usage.
- If I get my sort working, the "rayon" library will make it utterly painless and safe to split up the recursive calls over multiple CPUs.
Overall, this has been a pretty fun experience. I'm not sure whether the finished product will use unsafe Rust in the inner loop. But the combination of generics, rayon, and tightly integrated test/bench/fuzz tooling has made this a very pleasant experience.
Verdict: I am entirely happy using Rust for hot loops. (Even if my current fastest inner loop does use "unsafe" to bypass a few bounds checks I'm not clever enough to eliminate otherwise.)