Skip to content

Stack space in async closures is not shared between branches in debug builds  #72247

Closed
@khuey

Description

@khuey

Consider:

#![feature(test)]
use std::future::Future;
use std::hint::black_box;
use std::pin::Pin;

#[derive(PartialEq, Eq)]
enum Kind {
    Thing1,
    Thing2,
    Thing3,
}

impl Into<i32> for Kind {
    fn into(self) -> i32 {
        match self {
            Kind::Thing1 => 42,
            Kind::Thing2 => -1,
            Kind::Thing3 => 0,
        }
    }
}

fn f1(kind: Kind) -> Pin<Box<dyn Future<Output = i32>>> {
    Box::pin(async move {
        match kind {
            Kind::Thing1 => return kind.into(),
            Kind::Thing2 => return kind.into(),
            Kind::Thing3 => return kind.into(),
        }
    })
}

fn f2(kind: Kind) -> Pin<Box<dyn Future<Output = i32>>> {
    match kind {
        Kind::Thing1 => Box::pin(async move { kind.into() }),
        Kind::Thing2 => Box::pin(async move { kind.into() }),
        Kind::Thing3 => Box::pin(async move { kind.into() }),
    }
}

fn f3(kind: Kind) -> Pin<Box<dyn Future<Output = i32>>> {
    Box::pin(async move {
        if kind == Kind::Thing1 {
            return kind.into();
        } else if kind == Kind::Thing2 {
            return kind.into();
        } else {
            return kind.into();
        }
    })
}

fn main() {
    let f_1 = f1(Kind::Thing2);
    let f_2 = f2(Kind::Thing2);
    let f_3 = f3(Kind::Thing2);
    black_box(Kind::Thing1);
    black_box(Kind::Thing3);
    black_box(f_1);
    black_box(f_2);
    black_box(f_3);
}

If you build this (on 2020-05-14 a74d186) you'll see that the stack frames for f1::{{closure}} and f3::{{closure}} are bigger than those for the f2::{{closure}s. If you try to add or remove a branch you'll also see that the size of f1::{{closure}} and f3::{{closure}} stacks vary accordingly.

This is frustrating because it means that branch heavy code blows out the stack much faster in a debug build than it does in an opt build when recursing. I saw an order of magnitude difference in stack size in one of my functions between debug and opt because of this.

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-async-awaitArea: Async & AwaitA-coroutinesArea: CoroutinesAsyncAwait-TriagedAsync-await issues that have been triaged during a working group meeting.C-enhancementCategory: An issue proposing an enhancement or a PR with one.I-heavyIssue: Problems and improvements with respect to binary size of generated code.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions