6 ms·
C being barebones does not mean it is faster. Because it has such weak typing and gives a huge amount of programmer freedom, compilers have to do a lot of work
by TazeTSchnitzel 6y ago
C being barebones does not mean it is faster. Because it has such weak typing and gives a huge amount of programmer freedom, compilers have to do a lot of work to be able to understand a C program well enough to optimise it.
Rust, on the other hand, requires the programmer to give the compiler more information about what they're doing.
A very simple example:
void foobar(struct foo *f)
{
f->a += 2;
foo();
f->a += 2;
bar();
f->a += 2;
}
Because C pointer types are so barebones, the compiler can't tell whether foo() and bar() can modify f->a just from looking at the above code. So it will always have to load and store that for each += operation.
Rust on the other hand has two kinds of reference, rather than pointers:
fn foobar(f : &mut foo) {
f.a += 2;
foo();
f.a += 2;
bar();
f.a += 2;
}
This is more high-level. But it's good for performance! Rust has a rule that you can only have one mutable reference to a struct at one time. Therefore, foo() and bar() can't be modifying f.a and it can simplify this to `f.a += 6;`.
(You can see it in action for yourself here: https://godbolt.org/z/hWs67P https://godbolt.org/z/hWs67P. Sadly, Rust doesn't do this by default due to problems with LLVM, but eventually it can.)
- pdimitar 6y agoLately on another Rust thread somebody pointed out that C programs use a lot of indirection like pointers and vtable dispatches which actually detracts from the supposed mega-speed of the low-level C. I found that to be mind-blowing and felt stupid for not remembering that earlier.
- andrepd 6y agoI don't think vtables are often used in C.
- codeflo 6y agoI think they meant function pointers in general (callbacks and the like, see qsort). Also, a lot of C codebases end up implementing manual vtables of some sort where polymorphism is required.
- howinteresting 6y agoDynamic dispatch (vtables are one way to do it, and so is a function pointer) is incredibly common in C, because there's no language-built-in way to perform monomorphization.
- rstuart4133 6y agoIt's true they aren't that common, but have you looked at the Linux kernel code? It's written in C, and vtables are everywhere. I've also used them on occasions. They do a particular job people call pluggable implementations / dynamic typing / sub-classing depending on their background. If there isn't some central place that knows all the types (if there is you can use switch/case/match), there isn't much choice, it's vtables or nothing. But needing to do that job is rare, so seeing it in languages that don't use vtables promiscuously to implement OO is correspondingly rare. Which is as it should be, because vtables are more expensive than a direct call, very expensive if they prevent inlining. One of rust's triumphs is it provides them, but you have to go out of your way to use them so it encourages avoiding their overheads.
- cygx 6y agoThat's what restrict is for: void foobar(struct foo *restrict f) { ... }
- TazeTSchnitzel 6y agoYes, C99 added that keyword, and you can use it in C code. (C++ is a more complicated matter, I believe…) But I think it's pretty uncommon in practice. One reason is that the C compiler has no borrow checker to help you notice when you're using `restrict` unsafely. (Also, there's the whole “strict aliasing” hell…)
- petschge 6y agoIt's not that uncommon in HPC codes. I.e. one of the places where speed matters.
- masklinn 6y agoAnd just like that we're back to > There's a significant difference between what these languages can achieve in theory, and how they're used in practice. Theoretically in C you can use restrict for this. Practically nobody does (rust users keep finding bugs and miscompilation in LLVM's noalias support), and it's a huge footgun because you're completely on your own and all bets are off if you misuse it. Meanwhile in rust land, it's the default and the compiler checks your homework, and while you can always lie to the compiler it's much rarer that you'd even get in a position to do so.
- howinteresting 6y agoBut restrict is very hard to reason about correctly, and the consequences for making a mistake are potentially catastrophic. There's a very real difference between what's possible in theory and what humans do in practice. I wish Hacker News people engaged more with the practice.
- ragnese 6y ago> Sadly, Rust doesn't do this by default due to problems with LLVM, but eventually it can. Can it, though? I keep hearing/reading FUD around both unsafe and Pin making it unlikely to ever be able to ubiquitously enable the noalias stuff.
- Jweb_Guru 6y agoCode affected by unsafe already has to go through `UnsafeCell` which disables most aliasing optimizations, and the stacked borrow semantics have explicit opt-outs for such cases. I don't believe there are any Rust semantic issues standing in the way of exploiting aliasing information in this way, just LLVM bugs (and the fact that its restrict model isn't currently equipped to handle the information at such a fine granularity).
- nyanpasu64 6y agoThere is undefined behavior in Rust affecting real-world code, including Tokio's scheduler, and code produced by async fn definitions. UnsafeCell doesn't solve the problem. There's more information at https://gist.github.com/Darksonn/1567538f56af1a8038ecc3c664a42462 https://gist.github.com/Darksonn/1567538f56af1a8038ecc3c664a.... Bug report at https://github.com/rust-lang/rust/issues/63818 https://github.com/rust-lang/rust/issues/63818. Reddit threads at (older) https://www.reddit.com/r/rust/comments/l4roqk/a_fix_for_the_llvm_noalias_bug_has_landed_in_time/gkq8wvg/ https://www.reddit.com/r/rust/comments/l4roqk/a_fix_for_the_... and (newer) https://www.reddit.com/r/rust/comments/lxw6cl/update_to_llvm_12_got_merged/gppvcy5/ https://www.reddit.com/r/rust/comments/lxw6cl/update_to_llvm.... Somewhat related HN thread at https://news.ycombinator.com/item?id=26406989 https://news.ycombinator.com/item?id=26406989.
- Jweb_Guru 6y agoThis is the `Pin<&mut>` example which I'm not that familiar with but was aware of. I think it's highly unlikely that this one, relatively niche use case is going to prevent Rust from ever being able to safely turn on aliasing optimizations. There have been several solutions proposed, e.g. adding a stricter UnsafeCell to the language; they may technically not be backwards-compatible, but given how `Pin` is used and the fact that this is a soundness issue, I think it should be fine. The HN thread is mostly unrelated. I agree that it would have been better to integrate `Pin` directly into the language, though, but mostly for ergonomic reasons; it could still probably happen in an edition upgrade.
- randomswede 6y agoIf you can have "one mutable" and "more than one non-mutable" reference (not sure if this is the case), you still cannot safely do that optimization, as foo() and bar() could in principle be reading f.a and the optimized version would then not have the correct values of f.a when foo() and bar() are called. Note, I genuinely don't know if this caveat applies to Rust. But, m general, not only mutable references need to be considered.