Live data from Hacker News

Is Rust stack-efficient yet?

arewestackefficientyet.com

31–40 of 111 posts

Re: Is Rust stack-efficient yet?

#31

Earlier quoted context omitted.

This is not about a heap-vs-stack tradeoff, this is about the compiler routinely generating very inefficient code that copies data around on stack for no good reason. For an example I've seen myself: using a custom GC pointer library, calling `Gc::allocate(SomeBigStruct{...})` constructed the SomeBigStruct on stack and copied it around using memcpy 4 times before it actually landed in the allocated heap memory. The e…

The optimizing backend for Rust and C++ is common. So if it didn't get optimized in Rust, it is very likely it wouldn't be in C++ as well. However, the code style of those two codebases might be different. IMHO C++ code is traditionally a lot more pointer and heap allocation heavy than Rust code. Rust makes moving stuff very convenient and using pointers/references quite inconvenient. Therefore showing heap allocatio…

> The optimizing backend for Rust and C++ is common. So if it didn't get optimized in Rust, it is very likely it wouldn't be in C++ as well. However, the code style of those two codebases

eh? Rust doesn't have placement-new or specify copy elision. nothing at all to do with backends or "code style".

Re: Is Rust stack-efficient yet?

#32
post #14

I noticed in one of my crates that Rust often cannot optimize "moves" away. I was in the unusual situation where I had to move around a very large stack buffer(typically in Rust they live on the heap). Instead of passing it back and forth between moving functions as I originally designed, I had to redesign it to use macros which significantly improved my benchmarks. Further attention to optimizations here would be ve…

How did you implement those macros?

Tbh it was like a year ago and I forget the specifics. You are welcome to look at the code, however:

https://github.com/nu11ptr/flexstr/blob/master/flexstr/src/b...

UPDATE: As I'm thinking about it, it is starting to come back to me a little:

1. I create the buffer

2. I do some op against it to fill it

3. I consume it and transform it into a final immutable flexstr

For #1, the 'new' function moves the buffer back to the caller (memcpy). Using it in #2 I think was fine as I think it is typically passed by mutable ref. For #3, the buffer was moved again (passed by `self`) so it could be consumed and reused without a language level copy (but was copying in the generated code). Replacing #1 and #3 with macros kept the stack buffer in the local stack frame and greatly sped up my code in benchmarks, and that is what those two macros do I linked to if I'm recalling correctly.

Re: Is Rust stack-efficient yet?

#33

Earlier quoted context omitted.

This is not about a heap-vs-stack tradeoff, this is about the compiler routinely generating very inefficient code that copies data around on stack for no good reason. For an example I've seen myself: using a custom GC pointer library, calling `Gc::allocate(SomeBigStruct{...})` constructed the SomeBigStruct on stack and copied it around using memcpy 4 times before it actually landed in the allocated heap memory. The e…

The optimizing backend for Rust and C++ is common. So if it didn't get optimized in Rust, it is very likely it wouldn't be in C++ as well. However, the code style of those two codebases might be different. IMHO C++ code is traditionally a lot more pointer and heap allocation heavy than Rust code. Rust makes moving stuff very convenient and using pointers/references quite inconvenient. Therefore showing heap allocatio…

> Move semantics is actually a quite modern thing in C++ (and not default like in Rust).

But it's very different. In Rust there are no move-constructors. A move is simply a memcpy. And the moved-from object doesn't have to be in "a valid, but unspecified state". You cannot use it, because the borrow checker prevents you from doing so. So it can actually be in any state, giving the compiler more room for optimization.

Re: Is Rust stack-efficient yet?

#35

Earlier quoted context omitted.

This is not about a heap-vs-stack tradeoff, this is about the compiler routinely generating very inefficient code that copies data around on stack for no good reason. For an example I've seen myself: using a custom GC pointer library, calling `Gc::allocate(SomeBigStruct{...})` constructed the SomeBigStruct on stack and copied it around using memcpy 4 times before it actually landed in the allocated heap memory. The e…

The optimizing backend for Rust and C++ is common. So if it didn't get optimized in Rust, it is very likely it wouldn't be in C++ as well. However, the code style of those two codebases might be different. IMHO C++ code is traditionally a lot more pointer and heap allocation heavy than Rust code. Rust makes moving stuff very convenient and using pointers/references quite inconvenient. Therefore showing heap allocatio…

What? If I understand correctly, the LLVM IR -> binary stage is shared. The Rust -> LLVM IR or C++ -> LLVM IR stages are obviously not shared, and these optimizations can happen there. Specifically C++ guarantees copy elision in some cases.

Re: Is Rust stack-efficient yet?

#36

That's why I like AVR8 assembly better than C for Arduino. You have this big register file and the one C wants to use the most is the stack pointer.

That's not the optimization in question here, though. C uses the stack pointer a lot if you use a lot of stack-local variables. Mark all your stuff "static" and it will use immediate addresses instead (which may or may not help you -- putting all the "local" stuff in a block referenced by one pointer is usually a good thing!).

What's happening here is that C++ is the inheritor of decades of ponderous analysis about how code works with temporary results such that it can usually (.../often/sometimes/under-the-right-astrological-sign) optimize them away or arrange to have them magically appear in the right place. The return value optimization and all the move semantics nonsense is aimed at this space.

As a result, C++ tends to put things on the stack "where they want to go", where I guess Rust is a little naive and needs to build them in one place just to copy them where they need to be.

Re: Is Rust stack-efficient yet?

#37
post #12

Earlier quoted context omitted.

The main goal is removing replacing pointless stack-to-stack copies with simply mutating in-place on the stack correctly in the first place. Due to some mix of: * Rust code relying more on copies than C++ (for esample, harder to make something uninitialized and fill it in) * LLVM missing optimizations that rust relies on heavier than C++ * No real guarantees around RVO / NRVO Rust code often will put something on the…

> No real guarantees around RVO / NRVO Shouldn’t Rust in theory have a lot more freedom in defining its calling conventions than C++ has? I wonder if there’s anything that prevents doing RVO by default, or if just hadn’t been a priority yet.

There was a RFC for them, but it didn't get much traction.

Re: Is Rust stack-efficient yet?

#39
post #38

Honest question - does this mean Rust isn't ready for production yet?

No. TFA points out there isn’t a gigantic performance sink or anything, just an infelicity in the code they’re generating.

>> Does this mean Rust is slower than C++?

> No. You can always write your Rust code carefully to avoid copies. Besides, all of this only comes out to a small percentage of the total instruction count. That being said, it's something we should fix, and which I'm working on.

Re: Is Rust stack-efficient yet?

#40
This affects the way you write code, too.

I was writing something in Rust and I wanted to create a new boxed object.

  Box::new(...)
Boom! Program crashes. The object I’m putting in the heap is too large for the stack. Rustc does this by instantiating the object on the stack, and then copying it to the box. I don’t really want to fuss with nightly or stuff like Box::new_uninit just to deal with this. C++ has both regular `new` and placement `new`, both of which put objects in memory which is already allocated. I had assumed that the Rust compiler could optimize out a move, since that’s such a prominent feature in C++ compilers.
Post reply on HN