New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reuse generator slots after StorageDead #52924

Open
cramertj opened this Issue Jul 31, 2018 · 4 comments

Comments

Projects
None yet
4 participants
@cramertj
Copy link
Member

cramertj commented Jul 31, 2018

Currently, generators won't ever reuse storage slots, causing them to take up more space than is necessary:

#![feature(generators)]

fn main() {
    let a = || { 
        {
            let x: i32 = 5;
            yield;
            println!("{:?}", x);
        }
        {
            let x: i32 = 5;
            yield;
            println!("{:?}", x);
        }
    };
    println!("{}", std::mem::size_of_val(&a)); // prints "12", could print "8" (4 for state, 4 for `x`)
}

Finding optimal solutions seems like a difficult packing problem, but it should be easy to do quite a bit better.

@RalfJung

This comment has been minimized.

Copy link
Member

RalfJung commented Nov 20, 2018

I am wondering if thinking of this as "slots of a struct" is the right model anyway. The content of the generator type (aside from the state flag) is basically the call frame for this generator, so shouldn't this be treated much more like the stack? There probably shouldn't even be types, just a large enough opaque "blob of bytes" that can be used for generator execution. If you think of this in terms of fields, you are not going to be able to reuse the space of two i16 for an i32 later, but there is no reason you shouldn't do that -- just like in { let a = 0i16; let b = 1i16; /* ... */ } let c = 2i32;, c can reuse the space that was previously occupied by a and b.

@cramertj

This comment has been minimized.

Copy link
Member

cramertj commented Nov 20, 2018

Yes, it's just a stack of bytes. This optimization was referring to the possibility to overlap some of the bytes to hold data that is currently stored in separate "slots of a struct".

@withoutboats

This comment has been minimized.

Copy link
Contributor

withoutboats commented Nov 20, 2018

My intuition about generator layout is that it would be:

  1. A discriminant large enough to hold a value for each yield point.
  2. The maximum number of bytes needed to hold the live stack variables at any yield points.

I expect that this is complicated by alignment and wanting to avoid moving values around inside of the generator as it iterates through states, but clearly we should be doing better than we are if the size is currently the sum of all yield point sizes instead of the max. If benchmarks are bad enough this may be a pre-stabilization priority. :-\

@cramertj

This comment has been minimized.

Copy link
Member

cramertj commented Nov 20, 2018

It's the sum of all variables that are alive across a yield point (one slot for every variable, not one slot for every yield point). We can do better by tracking which variables are live across non-intersecting sets of yield points and using the same space to store both.

@Nemo157 Nemo157 referenced this issue Nov 21, 2018

Open

Tracking issue for async/await (RFC 2394) #50547

1 of 10 tasks complete
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment