7 ms·
> If the work is done I expect Rust to be faster than C and C++ for the same reason that C++ can sometimes be faster than C: a more advanced type system can all
by killercup 6y ago
> If the work is done I expect Rust to be faster than C and C++ for the same reason that C++ can sometimes be faster than C: a more advanced type system can allow better optimization in many cases.
Every now and then I check in on whether LLVM can deal with rustc spamming "noalias" on all references. You can find the latest change in [1]. While in theory this unlocks _a ton_ of optimizations, noalias is used very rarely in C/C++ code so these compiler passes are not exercised a lot by existing LLVM tests and/or not realized in full.
[1] https://github.com/rust-lang/rust/pull/82834 https://github.com/rust-lang/rust/pull/82834
- api 6y agoThis is why I think Rust could eventually be faster than C and C++ for a lot of things. The work has to be done though. You're right that noalias enabled optimizations are neglected because you can rarely use them in C code. On the Rust side I think the language needs some way to annotate if's as likely/unlikely. This doesn't matter in most cases but can occasionally matter a lot in tight high performance code. It can allow the compiler to emit code that is structured so as to cause the branch predictor to usually be right, which can have a large impact.
- steveklabnik 6y agoThat's tracked by https://github.com/rust-lang/rust/issues/26179 https://github.com/rust-lang/rust/issues/26179 by the way.
- drran 6y agoWhy not just use PGO (Profile Guided Optimizations)? Sadly, PGO does not work with cross-language LTO, because of conflict of LLVM module name ('Profile').
- deleted 6y ago[deleted]
- amelius 6y agoA more advanced type system may also drive you into a corner where you make bad decisions wrt performance.
- titzer 6y agoCan you give an example?
- Arnavion 6y agoNot amelius, but one case that happened to me is that Rust requires wrapping a `T` in a `RefCell` if two closures use it as `&mut T`. This happens even if you the caller know that the closures are invoked from a single thread and do not invoke each other, and thus only one `&mut T` will be in effect at any time. This is because closures are effectively structs with the captures as fields, so both struct values (and thus both `&mut` borrows) exist at the same time even though their respective fields are not used at the same time. Not only do you have to use `RefCell`, but you now also have panicking code when the `RefCell` borrow "fails", even though you know it can't. rustc is also not smart enough to notice the exclusivity at compile-time and elid away the RefCell borrow flag test and the panic branch. fn foo(mut cb1: impl FnMut(), mut cb2: impl FnMut()) { for _ in 0..10 { cb1(); cb2(); } } let mut x = String::new(); foo(|| x.push_str("cb1,"), || x.push_str("cb2,")); https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=9b3aaad390070ffa3dcd0420203a0833 https://play.rust-lang.org/?version=stable&mode=debug&editio... Fixed using `RefCell`: https://play.rust-lang.org/?version=stable&mode=release&edition=2018&gist=b1d8781fc5d5962a40c485b3b148ff2d https://play.rust-lang.org/?version=stable&mode=release&edit... . Inspect the ASM and trace the use of the string constant "already borrowed"; you'll see it being used for the borrow flag test because it wasn't elided. The equivalent non-Rust program could use String pointers for the two closures. I'm not sure whether they could be noalias or not, but at the very least they wouldn't need to generate any panicking code.
- Diggsey 6y agoThe "used from a single thread" aspect is a red herring: RefCell can only be used from a single thread anyway, and the compiler enforces this statically. The "state" value in a RefCell is overhead, although it's fairly minor given that it doesn't need any synchronization to access. The extra panic branches are probably the largest overhead. That said, these overheads stem from Rust's safety guarantees rather than its strong type system: you can have a language with a strong type system that does not do these checks. Furthermore, there are of course ways to avoid this overhead within safe Rust: if you can use the type system to prove that the cell cannot be borrowed at the same time, then you don't need to do the checks, and in that sense a strong type system can actually help avoid overheads that were introduced by being a safe language.