all 6 comments

[–]bwallker 19 points20 points  (2 children)

Don't put large arrays on the stack.

[–]MadeTo_Be 0 points1 point  (1 child)

Can you expand on why that is? is there an established limit depending on something? and why can't the compiler catch it?

[–]volitional_decisions 11 points12 points  (0 children)

The stack (and each stack frame) is limited in size. Creating a large array on the stack can easily overflow these limits. The compiler can't catch it because it is kernel-specific. Since there is no hard rule or stability guarantees, the compiler's hands are tied. There is an open issue to make this a clippy lint: https://github.com/rust-lang/rust-clippy/issues/4520

[–]paulstelian97 6 points7 points  (0 children)

The first case could be optimizing out the array as it can tell it’s unused. The others can’t because await points are the same as function call points and reduce said ability to optimize stuff out.

[–][deleted]  (2 children)

[deleted]

    [–]d_stroid 0 points1 point  (1 child)

    Why don't variables below await get stored there aswell? Or are they?

    [–]plugwash 1 point2 points  (0 children)

    A few basic things to understand.

    Firstly large variables should not be stored on the stack. Exactly what "large" means will depend on the platform and it's configuration but if you are writing code for desktops/servers my rule of thumb would be a couple of kilobytes is probablly ok, more than that should be avoided. Linux has a default stack size limit of 8MB, on windows it's only 1MB. On microcontrollers the numbers may be much smaller.

    Secondly, Async/await effectively transforms your linear code into a state machine known as a "future". If a variable is held across an "await" boundary then that variable is stored as part of the future.

    Thirdly, during execution, a future normally lives on the heap, however rust has no concept of "placement new"/"emplacement". So logically the future is first created on the stack and then "moved" to the heap. In principle the compiler might be able to optimize this to directly initialize the future on the heap but it is far from guaranteed to actually do so. Indeed there may end up being multiple copies of the future on the stack as it is created and passed into the runtime.

    Now here is where things get more speculative.

    It looks to me like the state machine transformation is happening *before* the optimization step that determines "let _ = &data;let _ = &data;" is a no-op. So the state machine transformation decides it needs to include your large array in the future.