It's great when something new and better comes along if that's really the case, but I've seen the "incrementally better in certain use cases" play many times before.
It's great when something new and better comes along if that's really the case, but I've seen the "incrementally better in certain use cases" play many times before.
Also even a 10x speedup sounds much. In Rust, instead using .get_unchecked() (the C++ equivalent) instead of using .get() is typically only a neglegible improvement of a few percentage points in that particular loop or whatever.
What makes you think you can't control how memory is managed in Rust? Rust doesn't have "automatic" memory management, it has a compiler that can help ensure you are managing memory correctly, and force you to type "unsafe" when you are doing things it doesn't understand.
Well it's a bit more subtle than that if we're honest.
Arguably, Rust does make a number of memory layouts (self referential structs, per struct allocators, non thread local addresses, etc) much harder to accomplish than "typing unsafe".
Per-struct allocators are a work in progress (see https://github.com/rust-lang/wg-allocators/issues/48).
Not sure what "non thread local addresses" means, but in my experience Rust is pretty good at sending data between threads (without moving it).
I have a hard time seeing how you could use self references without a combination of raw pointers, pins/projects, and unsafe code. The tediousness of doing so is pretty much a no-go for any sane developer.
The only sane solution seems to be a generous sprinkle of Arcs, which is _not_ okay in high performance scenarios.
> Per-struct allocators are a work in progress
Yes, and most of us don't really want to use nightly in production. It's been years of work on the allocator work group already, and there's probably still years to wait before a stable release.
> Not sure what "non thread local addresses" means, but in my experience Rust is pretty good at sending data between threads
Well I mean overall any way to allow a somewhat eased setup for storing and retrieving objects to/from shared memory, across processes that do not map said memory at the same location. This is very very annoying to implement in Rust at the moment.
Don't get me wrong though, I think Rust has a lot to offer. But when you dive in even slightly technical subjects in Rust, it soon becomes obvious that the power of C/C++ is far from "just type unsafe" away.
You just do it. I can't recall a good example at the moment, so I've just thrown together a load of Cells: the thing I was doing when I learnt this technique didn't have any Cells in it.
https://play.rust-lang.org/?version=nightly&edition=2021&gis...
#[derive(Debug)]
struct SlotMachine<'a> {
slots: Vec<Cell<i32>>,
current: Cell<Option<&'a Cell<i32>>>,
}
fn main() {
let machine = SlotMachine {
slots: vec![Cell::new(0); 5],
current: Cell::new(None),
};
machine.current.set(Some(&machine.slots[4]));
machine.slots[4].set(12);
println!("{:#?}", machine);
}
> Yes, and most of us don't really want to use nightly in production.Strictly speaking, this is only needed if you want to use Rust's standard library types with custom allocators. You've been able to have per-struct allocators for your own types since long before I learnt the language.
> Well I mean overall any way to allow a somewhat eased setup for storing and retrieving objects to/from shared memory, across processes that do not map said memory at the same location.
Can't you just store offsets into the memory region, and PhantomData references, then unsafely index the mmap'd region when you need an actual reference? Seems like the same thing you'd do in C, except the function you abstract that with can be a method instead. (Unless I'm still misunderstanding.)
experienced C++ programmers says "Rust is great but very young and still C++ is better for my use case".
Rust supporter replies "what makes you think you know better than us what it is better for you?"
Welcoming community, they said.
Read again
> I optimize directly for the hardware I'm running on, which typically gives me 10-100x performance improvements. Controlling how memory is managed is critical.
> What makes you think you can't control how memory is managed in Rust?
> Arguably, Rust does make a number of memory layouts (self referential structs, per struct allocators, non thread local addresses, etc) much harder to accomplish than "typing unsafe".
So basically, the right question would be "can you explain what you mean that Rust can't control how memory is managed"?
Because the author knew, the Rust supporter didn't and confused "work in progress" with "I need it now because I'm using it now in production in my daily job"
IMO if you’re writing a highly concurrent or parallel application it’s hard to deny how much more productive and safer it is to write it in Rust. Optimizing cpu instructions in a single thread? Rust might just be an alternative and not competitive.
You can't replace C++:
1. Well-functioning, mission-critical systems that were made in C++ aren't being replaced.
2. Anything GPU and ML-related is still better solved in Python/C++.
3. Anything that depends on scalable, high-performance data structures, C++ still has a better selection.
What "replace C++" can mean is: There are use-cases where Rust and C++ overlap where the Rust story is maturing, and you can pick Rust in those cases instead of C++ if you're not already invested in C++. But either if you have existing C++ code that works well, or you have deep C++ knowledge, or you are in one of the categories where Rust is not on par yet, there's still no competition.
Very true. There is a lot of existing working code that is not going away and will even still need to be maintained for decades.
> 2. Anything GPU and ML-related is still better solved in Python/C++.
Can be done in Python/Rust
> 3. Anything that depends on scalable, high-performance data structures, C++ still has a better selection.
Why is that? Rust has equally high performance data structures. Even more perfortant in some cases (restrict by default, destructive move, easier concurrency)
Can be done in Brainfuck. You just need to write the Cuda bindings.
> > C++ still has a better selection.
> Why is that?
Because C++ was around for longer.
For people who are more invested in Rust (myself included), extending Rust's selection is seen as a great endeavour.
I personally keep a list of things I wish to "oxidize". :-)
As far as I know, the Rust developers consider performance worse than C a bug.
I'm thinking of things like Herb Sutter's deferred_heap (https://github.com/hsutter/gcpp) that give you GC-like abstraction. It's pretty cool that this is possible to write in vanilla C++ with decent ergonomics. I tried to make something similar awhile back in Rust and hit a wall in terms of making something that would be pleasant to use.
Rust has several on-going experiments with making a nice GC, along with good support for arenas. (If you can use arenas, they're great.)
Rust goes as low-level as C (there's literally c2rust source-to-source translator). It gives you full control over all allocations and indirections. It's even more efficient than C++ in a few places, e.g. it has a more efficient ABI for unique_ptr, doesn't call destructors redundantly after a move, doesn't need isa pointer in structs with virtual methods.
Rust generates native code using LLVM, and even has a bit stricter semantics that allow better optimizations (notably immutability and mutable aliasing are stricter than in C and C++).
It seems like a straightforward trade between speed and safety to me, but nobody in the Rust community wants to admit it. As such, there aren't a lot of public benchmarks of applications out there that can help the rest of us figure out when we actually should use rust - when the performance is worth the safety (no, Rust evangelists, that kind of safety is not always worth it, and no, we don't always pay the same costs for it).
The examples I have seen where a Rust rewrite is faster than original C++ code usually have glaring performance issues in the C++: Often, it is code that does a ton of unnecessary copies. The Rust code often doesn't copy as much because the Rust language makes that kind of copying inconvenient or because the person doing the rewrite is a Rust expert and not a C++ expert. The other kind of speedup I have seen is when people swap a C++ std::map for a Rust btree_map, and don't realize that absl::btree_map (a C++ btree map in Google's library) is what Rust's btree_map is based on.
I haven't seen any examples where C or C++ code written with performance in mind gets a rewrite to Rust and goes faster, and I have seen the opposite.
Cliff Biffle's great series of blog posts called "Learn Rust the Dangerous Way" takes the fastest C program from a benchmark game entry, rewrites it naïvely in Rust using copious amounts of `unsafe`, and then transforms it into program without `unsafe` while keeping it idiomatic. The last version is faster than the C implementation.
It's a toy example but it's real-world example.
However, the benchmarks game (and microbenchmark-based comparisons in general) is hard to take seriously if you are thinking about application performance. Some languages microbenchmark very well, but don't translate that to system performance (C is the poster child of this effect), and some microbenchmark poorly but work very well in practical systems (Go is the most popular language with a big gap here, but some functional language like OCaml or Haskell probably has the biggest gap).
The reasons for these gaps can include things like it being harder to use the optimal data structure for your application (eg C code using red-black trees instead of btrees in 2023) and large code size causing terrible caching behavior (heavily templated C++). I also remember seeing something here about some non-optimal calling convention in the Rust compiler, which would be another thing that shows up in a system that doesn't in a microbenchmark.
Microbenchmarks are not a good replacement for system-level comparisons.
Even when we're shown "Benchmarks are a crock" ?
https://benchmarksgame-team.pages.debian.net/benchmarksgame/...
> Microbenchmarks are not a good replacement for system-level comparisons.
For example —
https://benchmarksgame-team.pages.debian.net/benchmarksgame/...
Instead, the main arguments for the claim that Rust is the same speed as C++ are microbenchmarks, which actually are pretty useless when you are comparing very different implementations. The time they are useful is when you are refining an implementation.
If you want to scrutinize the benchmarks game even further, they use gcc for their c compiler, where clang would probably be a better choice since the benchmarks are arithmetic-heavy.
> The reasons for these gaps can include things like it being harder to use the optimal data structure for your application (eg C code using red-black trees instead of btrees in 2023)
Bryan Cantrill described this experience[1] and maybe that is exactly what you are referring to.
But if you're just looking for a language to get work done with, does it matter if Rust is faster because of better off-the-shelf data structures and algorithms or because of some inherent magic in the programming language?
[1] See point 9 here: http://dtrace.org/blogs/bmc/2018/09/18/falling-in-love-with-...
I don't already know C++. For me, learning Rust is easier than learning safe C++. I imagine that there are many people in a similar situation for whom Rust makes more sense than C++. That doesn't mean that it makes more sense for everyone.
And would cost someone else real money to do — so microbenchmarks ;-)
> … they use gcc for their c compiler, where clang would probably be a better choice…
https://benchmarksgame-team.pages.debian.net/benchmarksgame/...
"Milli benchmarks are not really hard
Micro benchmarks are challenging, but OK
Nano benchmarks are the damned beasts!"
Slide 19
For the companies that re-wrote their databases, login systems, and other similar things in Rust, a real benchmark comparison would be pretty easy, and they probably did it internally anyway. Hook one of your servers up to an artificial load generator and see how much it can take.
Personally, I read that as "can be as fast as, but without you having to be Willy Tarreau level genius" which is all I need.
It's easier/more-intuitive to do a lot of things in C++, but safe, high performing C++ is certainly harder than safe, high performing Rust for huge swaths of use-cases. Also, as has been mentioned, its type system that benefitted from the PL research since the 80s also allows for nicer expression of business logic. In particular, this means that in Rust, unlike C, Go, or even C++ in great part, you are not writing in the same low-level intricate language at every level of your stack i.e. it can be a nicer high-level experience the higher you go if you designed your lower tiers well.
And that last thing to me is the biggest advantage it has over the competition.
Off course, there is also the fact that juggling dependencies in a non-trivial C++ project was a nightmare until recently with vcpkg and it's manifest mode and that will take probably another decade to become commonplace in the ecosystem (if ever).
[1]: https://github.com/bparli/convey [2]: https://bparli.medium.com/adventures-in-rust-and-load-balanc...
https://benchmarksgame-team.pages.debian.net/benchmarksgame/...
Which is my biggest gripe with benchmark games. There really ought to be strict categories for "straightforward, idiomatic code someone with 3 YoE could write", "optimized-but-still maintainable code an experienced senior engineer would write", and "unrestricted wizardry".
> … straightforward, idiomatic code…
Even with tiny programs, once you ask for "idiomatic code" things stop being straightforward: different languages do things differently —
https://benchmarksgame-team.pages.debian.net/benchmarksgame/...
> … hand-written SIMD…
One approach is to filter those programs into their own section — hand-written vector instructions | "unsafe".
Another is to use source code size as a proxy —
https://benchmarksgame-team.pages.debian.net/benchmarksgame/...
But it would be interesting to try to approximate something like those categories. The project repo[1] contains the following call for idiomatic code:
Please, people ask to see more "idiomatic" programs —
- we already have enough exhaustively optimized Rust and C programs.
- we already have enough hand-written vector SIMD and "unsafe" programs.
Thank you.
[1] https://salsa.debian.org/benchmarksgame-team/benchmarksgameOn this subject... it's much easier to become a Rust expert than a C++ expert.
I do expect the world to have more Rust experts than C++ ones today, even with much lower usage of the language and much less time for people to learn it.
Often, code written in C / C++ does unnecessary copies precisely because the risk of avoiding those copies is hours of debugging, crashes in production, or security flaws.
So this is a legitimate benefit of Rust. Being able to code more aggressively up front, without fear of something blowing up in your face later, or much later when the intern touches the wrong line.
>I haven't seen any examples where C or C++ code written with performance in mind gets a rewrite to Rust and goes faster, and I have seen the opposite.
Here's one from Bryan Cantrill
http://dtrace.org/blogs/bmc/2018/09/28/the-relative-performa...
The point he makes is that while Rust is not strictly faster than C in most cases, it gives you the comfort of being able to use more highly optimized libraries and data structures without needing to have the extreme level of trust in the author that you do in C / C++. This is basically the same argument.
Compared to C, where a data structure library is at best a heap of cobbled-together macros that make a lot of assumptions about the underlying types, C++ has well-developed libraries for all of the data structures mentioned.
And you need to trust the author plenty to make a btree that works - that is a very complicated data structure. It's not just about memory safety, there can be a lot of functional bugs, too. The author of the C++ one is Google/Facebook, who I trust to write a solid library, while the author of the Rust one is someone who was inspired by the C++ version.
At this point, both are well-tested, but there are a lot of Rust libs that are in the same position, but relatively untested.
The language-change whiplash that people accuse Rust of happens to C++ folks who leave the language for several years, since "idiomatic" C++ has changed substantially over the last 20 years.
EDIT - Just to clarify, there are SO MANY different ways to do things in C++ that the way to really learn C++ is by reading (both good code and the spec), not by writing. It's very easy to write a ton of code and not actually do it in the fastest/best/most idiomatic way. This is complicated by the fact that a ton of C++ examples out there are wrong. It's really an electrical engineer's language at this point - read all the datasheets if you want to avoid getting shot in the foot (and still get shot in the foot anyway).
E.g., will the C++ rewrite for X improvement take longer, or require a more highly skilled programmer, or would the Rust programmer require some high level of expertise to yield the results but a junior C++ guy/gal could knock it out quickly?
Has anyone done real comparisons of this sort, such as taking software package X and assigning a performance rewrite to a junior Rust dev, a senor Rust dev, a junior C++ dev and a senior C++ dev, giving each a couple weeks then checking the yielded performance?
Also, how does this interact on teams? It's been a long time since I've written any C++, and I've not tried Rust, but it seems some of the Rust safety features make it harder for devs to step on each other's toes.
I thought the safety was compile time optimization and semantic analysis, to enable code generated to feature performance optimization that reflects in improved performance at runtime.
So, where are you seeing performance penalties?
My intuitive guesses would be that the culprit is actually either larger code size, worse code layout, being unable to optimize certain code sections to the same level (undefined behavior in C++ is actually helpful for speed, and not all of it is actually unsafe), something about how calling conventions or struct layouts differ, or another "second-order" effect.
Spoiler: the end result uses idiomatic Rust, doesn't use unsafe and is faster than the hand-optimized version in C (and hand optimized one in Rust).
https://benchmarksgame-team.pages.debian.net/benchmarksgame/...