Hacker News new | past | comments | ask | show | jobs | submit login

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. Sadly, Rust doesn't do this by default due to problems with LLVM, but eventually it can.)




Lately 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.


I don't think vtables are often used in C.


I 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.


It'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.


Dynamic 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.


> 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.


You have correctly identified it as FUD. People have a bone to pick with Pin, so they irrationally latch on to it, but the general problem is the fact that the &mut invariants cannot currently permit any self-referential data, which is a useful concept in general (for intrusive data structures, etc) and whose lack that people have been hacking around since before 1.0, with crates like rental and owning_ref. The plan to fix this is to make self-referentiality a first-class concept in the language, as a principled exception to the usual &mut uniqueness invariant that preserves memory safety while properly encoding the aliasing guarantees.


> The plan to fix this is to make self-referentiality a first-class concept in the language, as a principled exception to the usual &mut uniqueness invariant that preserves memory safety while properly encoding the aliasing guarantees.

Are there issues I can subscribe to or RFCs for this?



Source? I would like this to be true, but I haven't actually seen it anywhere.


https://news.ycombinator.com/item?id=26410487 (it's a significant part, though not the entirety, of this comment)



Code 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).


There 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/1567538f56af1a8038ecc3c664a....

Bug report at https://github.com/rust-lang/rust/issues/63818.

Reddit threads at (older) 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....

Somewhat related HN thread at https://news.ycombinator.com/item?id=26406989.


This 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.


The github discussion thread where that originates from also contain the language designers optimistically discussing how in the worst case `Pin` just has to be hardcoded to exclude these optimizations. Which wouldn't make it the first type in the std lib that works similary (see interior mutability and UnsafeCell), so I personally intepret that as "unlikely to be an issue".


That's what restrict is for:

    void foobar(struct foo *restrict f) { ... }


And 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.


But 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.


Yes, 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…)


It's not that uncommon in HPC codes. I.e. one of the places where speed matters.


If 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.




Consider applying for YC's Spring batch! Applications are open till Feb 11.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: