C is the wrong language to compare to. More precisely, Rust should be able to beat C, routinely. Presumably it will, when it gets more optimizer attention. With any luck, that improvement can go into LLVM proper, and speed up many other languages besides. Why should Rust beat C? First, it does not suffer from the pointer aliasing faults C has. Second, const really means const, where in C the compiler has to assume an…
”even though C++ suffers from the same aliasing flaws C has.” C no longer has them. Not since a standard that’s 20 years old (C99). restrict qualifier handles that. Unfortunately C++ committee has not included restrict in C++, but it is available in all major compilers as an extension.
Why is Rust slightly slower than C?
151–160 of 251 posts
Re: Why is Rust slightly slower than C?
#152Re: Why is Rust slightly slower than C?
#153Earlier quoted context omitted.
I know, the meaning of "zero cost abstraction" is really "zero cost to not use the abstraction." But that's like saying if you don't use rust you don't pay for it. Just because there is the unsafe escape hatch in the language, you don't get to label the language as zero cost abstraction. Because practically speaking there is a lot more runtime cost to rust than people will tell you. Many langauges (almost anything wi…
You still seem to be missing the point. If you use a Rust construct that does bounds-checked accesses, you're explicitly using bounds checks. You're not paying for anything beyond the bounds checks that you're using. If you use a Rust construct that elides the bounds checks (e.g. get_unchecked), there are no bounds checks, and you don't pay any cost for the fact that Rust supports a bounds-checked subscript operator.…
1. Use a C-style raw pointer. This has no overhead but is unsafe.
2. Use a reference counted or garbage collected pointer. This provides safety but is a non-zero-overhead abstraction. In other situations, reference counting can be necessary to manage unpredictable object lifetimes, but in this case we're assuming it would only be used to guard against mistakes, so the cost represents overhead. Even if some language implements reference counting just as fast as you can implement reference counting in C, that doesn't make it zero-overhead.
But Rust adds a new option:
3. Use a borrow-checked reference. This is safe, yet zero-overhead compared to the unsafe option, as long as your usage pattern can be expressed in terms of Rust lifetimes.
Going back to array indexing, the analogous situation is that you want to access an index that you, the programmer, expect to always be in bounds. Option 1 corresponds to unchecked indexing, and option 2 corresponds to bounds-checked indexing. But Rust brings nothing new in this area: there is no option 3.
Yet an option 3 is theoretically possible: safe unchecked indexing if you can prove to the compiler that the index can never be out of bounds. This can be done in languages with dependent types or built-in proof systems. (It can even be done in Rust with a certain neat hack [1], but with a lot of limitations.)
I'm not saying Rust is bad for not having dependent types. As far as I know, it's an open research problem how to make those features feel ergonomic and easy to use, even to the extent that Rust lifetimes are (i.e. not totally). And on the flipside, bounds checks usually don't cause very much overhead in practice, thanks to CPU branch prediction, plus the optimizer can sometimes remove them.
But I'd say that Rust's choice not to go there (...yet...) justifies calling its current approach a non-zero-overhead abstraction.
Re: Why is Rust slightly slower than C?
#154Earlier quoted context omitted.
You still seem to be missing the point. If you use a Rust construct that does bounds-checked accesses, you're explicitly using bounds checks. You're not paying for anything beyond the bounds checks that you're using. If you use a Rust construct that elides the bounds checks (e.g. get_unchecked), there are no bounds checks, and you don't pay any cost for the fact that Rust supports a bounds-checked subscript operator.…
What part of my comment made you think I didn't understand that? Was it when I wrote: "it really [is] zero cost to not use the abstraction"? Or when I wrote: "if you don't use rust you don't pay for it"? I understand the concept fine. It is rust's marketing gimmick and not a useful tool to describe the language. Rust is no more "zero cost" than C++, Fortran, Ada, Objective C, and even D can even make the claim to som…
C++ popularized the term. And if C++ is commonly described as having designed its features to be zero-cost (or you only pay for what you choose), there's nothing wrong with Rust describing the same concept with the same term.
Re: Why is Rust slightly slower than C?
#155Looking at performance counter data is good, but I would have liked to see a real validation of the hypothesis that bounds checking is to blame for the extra branches and instructions. That is, modify the Rust compiler to not emit bounds checks (or maybe there is even a flag for this?) and look at performance and counters. I would imagine that this would bring the data for Rust to pretty much the same as C. But other…
> only 2% - 10% yeah, I wonder in which world they live. If I could sell a limb to get 10% more audio plug-ins in my DAW, you could be sure that my bedtime book would be "Life pro-tips for quadruple amputees"
Re: Why is Rust slightly slower than C?
#156Earlier quoted context omitted.
You still seem to be missing the point. If you use a Rust construct that does bounds-checked accesses, you're explicitly using bounds checks. You're not paying for anything beyond the bounds checks that you're using. If you use a Rust construct that elides the bounds checks (e.g. get_unchecked), there are no bounds checks, and you don't pay any cost for the fact that Rust supports a bounds-checked subscript operator.…
What part of my comment made you think I didn't understand that? Was it when I wrote: "it really [is] zero cost to not use the abstraction"? Or when I wrote: "if you don't use rust you don't pay for it"? I understand the concept fine. It is rust's marketing gimmick and not a useful tool to describe the language. Rust is no more "zero cost" than C++, Fortran, Ada, Objective C, and even D can even make the claim to som…
This part:
> But that's like saying if you don't use rust you don't pay for it. Just because there is the unsafe escape hatch in the language, you don't get to label the language as zero cost abstraction.
Rust isn't "zero-cost" because of the unsafe hatch; that's completely orthogonal. It's zero-cost because if you don't use a feature you don't pay for it. The fact that you need unsafe to get non-checked subscripting isn't particularly relevant to the fact that using non-checked subscripting in Rust means you're not paying for the existence of checked subscripting.
> Under your description an opt in generational GC is zero cost.
You're conflating implementation with semantics. If you have a choice between different allocation strategies that all result in the same observable runtime behavior, using a garbage collector over manual alloc/free is a cost. With manual alloc/free there's no runtime tracking to determine when an object should be freed, it's entirely static. Using a GC dramatically simplifies this from the developer's perspective and avoids a whole class of bugs, but comes with a runtime cost. Meanwhile for single-owner models, Rust's Box type has no runtime overhead compared to alloc/free, since there's no runtime tracking, the alloc and free points are determined statically by the compiler.
Re: Why is Rust slightly slower than C?
#157Earlier quoted context omitted.
You still seem to be missing the point. If you use a Rust construct that does bounds-checked accesses, you're explicitly using bounds checks. You're not paying for anything beyond the bounds checks that you're using. If you use a Rust construct that elides the bounds checks (e.g. get_unchecked), there are no bounds checks, and you don't pay any cost for the fact that Rust supports a bounds-checked subscript operator.…
I don't entirely agree. As an analogy, suppose that you want to pass a pointer to an object as a function argument. As the programmer, you expect the object will necessarily remain allocated as long as the function uses it, since it's kept alive elsewhere, but you might have made a mistake. Before Rust, your options would be: 1. Use a C-style raw pointer. This has no overhead but is unsafe. 2. Use a reference counted…
// C safe subscript
if (i
That's the abstraction, and Rust introduces no overhead here.Re: Why is Rust slightly slower than C?
#158Earlier quoted context omitted.
Another thing to point out: our 6% - 11% difference is already quite low. Netbricks [1] has a similar comparison between a Rust and a C network function (only the NF, driver in C in both cases) and they find 14% (LPM) to 20% (synthetic NF) difference. Fun fact: we've a system where Rust is faster than C despite still using more instructions, so yeah, neither instructions nor cycles tell the whole story... [1] https:/…
> Fun fact: we've a system where Rust is faster than C despite still using more instructions I think Bryan Cantrill did a pretty good explanation on differences you might see when rewriting something in Rust[1], and one of the things he looked at to see what was going on was Cycles Per Instruction. Instruction count itself means little if the instructions themselves have very different performance profiles and requir…
Re: Why is Rust slightly slower than C?
#159Earlier quoted context omitted.
My experience is old, but my summary point remains: Language defined and compiler supported functionality usually is faster than libraries of external functions. The relevance here: C isn't the fastest thing around and can be beaten. Maybe similarly for Rust. For PL/I and strings, for the IBM versions on S/360 and S/370, the compiler might have used some of the hardware instructions for string handling. I'd be surpri…
The compiler (off the top of my head this applies to GCC, clang, and MSVC, dont know about ICC) generally understands the C string functions at a fundamental level and will emit optimized code instead of a function call that will use hardware instructions and might know the size of the buffer at compile time. For instance on GCC you have to specify -fno-builtin to say "you don't know what you think you know about c l…
C is the greatest. The greatest, best ever. The greatest, fastest of all time, never to be improved on. Pure genius. Pure gold. And C programmers are like people who climb Mount Everest barefoot with no tools and the only REAL programmers.
Yup, C religion.
My view, bluntly: C was a toy language for an 8 KB DEC computer, well known to be a toy at the time. It's still a toy language and so primitive that it is a huge waste of effort in computing. There's no excuse for it and hasn't been even from the first day it was written, even for operating system code, since even at the time Multics was written in PL/I. And C is so primitive, in its K&R definition, that for string and array functionality, it is SLOW.
Besides, the idea that we are stuck-o with strcpy, etc. as in the original K&R external library means that we can't implement array bounds checking, reports on memory usage, etc. with an upgraded library of external functions.
My points here are rock solid: Functionality defined in the language and implemented in the compiler can be a lot faster than implementations with libraries of external functions. On strings, C and Fortran are examples. On arrays, C is an example.
If Rust is not a lot faster than K&R C, then I'm surprised at Rust.
All those hero barefoot Mount Everest climbers might notice that they are really tired, their feet hurt, and they very much need some better equipment!
Ah, programming language religious wars!!!!!
Grow up HN; set aside the toys of childhood. Be rational and set aside religious wars.
Re: Why is Rust slightly slower than C?
#160Earlier quoted context omitted.
Their initial explanation is bounds checking specifically. While most abstractions are zero-cost, bounds checking isn't. I do wish there were a way to track expectations about integer ranges in a type system, but there currently aren't.
Bounds checking is a “zero cost abstraction” in the sense that it is both “pay for what you use” and “you couldn’t implement it yourself any better.” That said, the implemention is not as fast as it theoretically can be, because the compiler isn’t always removing bounds checks that are provably true. When const generic lands, you will be able to make those assertions! A basic implementation is in nightly.
One can argue that any feature is a zero-cost abstraction for the exact set of trade-offs it has (in the limit: "you couldn't implement a feature that executes this exact sequence of instructions 'mov ...' any better if you did it by hand").
I think focusing on a hypothetical perfect coder is more useful, someone who never fails an assertion or bounds check. This person does not need any safety/convenience features (unlike us real humans), but they can still use some such features without a penalty. Bounds checks are not one of them. (But... At least they don't have much of a pervasive/whole program cost.)