Skip to content
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

Use closure for init constraints #2939

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

CanardMandarin
Copy link
Contributor

Hello,

This PR is somewhat related to issues #2915 and #2920.
It seems that with Anchor 0.29, stack usage in try_accounts increased and more easily causes Stack offset of X exceeded max offset of 4096 by X bytes, please minimize large stack variables error.

Existing projects using Anchor could have trouble migrating, and it looks like this question appears a lot in Discord.
Using closures for init constraints helps decrease the stack usage and helps mitigate the issue.

What do you think?

Copy link

vercel bot commented Apr 28, 2024

@CanardMandarin is attempting to deploy a commit to the coral-xyz Team on Vercel.

A member of the Team first needs to authorize it.

Copy link
Collaborator

@acheroncrypto acheroncrypto left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using closures for init constraints helps decrease the stack usage and helps mitigate the issue.

That's definitely interesting because using map_err and creating a closure uses more stack space compared to regular match statements. I'm also not sure if this behavior will always be the same with all Rust versions.

Is it possible to add a test that demonstrates the improvement in stack usage?

@nabeel99
Copy link
Contributor

nabeel99 commented May 1, 2024

@acheroncrypto @CanardMandarin , are closures always guranteed to be #[inline(never)] ? since that is what we want to achieve any solution that moves all this to a seperate stack frame. this solution is fine as long as the above gurantee is meant to hold.

@nabeel99
Copy link
Contributor

nabeel99 commented May 1, 2024

so i tested it out with the following struct , reduced stack usage from 15k to 4k.

#[derive(Accounts)]
pub struct UpdateFooSecond<'info> {
    #[account(
        mut,
        constraint = &foo.load()?.get_second_authority() == second_authority.key,
    )]
    foo: AccountLoader<'info, Foo>,
    second_authority: Signer<'info>,
}

#[derive(Accounts)]
pub struct CreateBar<'info> {
    #[account(
        init,
        seeds = [authority.key().as_ref(), foo.key().as_ref()],
        bump,
        payer = authority, owner = ID,
        space = Bar::LEN + 8
    )]
    bar: AccountLoader<'info, Bar>,
    #[account(
        init,
        payer = authority, owner = ID,
        space = Bar::LEN + 8
    )]
    bar_3: AccountLoader<'info, Bar>,
    #[account(
        init,
        payer = authority, owner = ID,
        space = Bar::LEN + 8
    )]
    bar_4: AccountLoader<'info, Bar>,
    #[account(
        init,
        payer = authority, owner = ID,
        space = Bar::LEN + 8
    )]
    bar_5: AccountLoader<'info, Bar>,
    #[account(
        init,
        payer = authority, owner = ID,
        space = Bar::LEN + 8
    )]
    bar_6: AccountLoader<'info, Bar>,
    #[account(
        init,
        payer = authority, owner = ID,
        space = Bar::LEN + 8
    )]
    bar_7: AccountLoader<'info, Bar>,
    #[account(
        init,
        payer = authority, owner = ID,
        space = Bar::LEN + 8
    )]
    bar_8: AccountLoader<'info, Bar>,
    #[account(
        init,
        payer = authority, owner = ID,
        space = Bar::LEN + 8
    )]
    bar_9: AccountLoader<'info, Bar>,
    #[account(
        init,
        payer = authority, owner = ID,
        space = Bar::LEN + 8
    )]
    bar_10: AccountLoader<'info, Bar>,
    #[account(
        init,
        payer = authority, owner = ID,
        space = Bar::LEN + 8
    )]
    bar_11: AccountLoader<'info, Bar>,
    #[account(
        init,
        payer = authority, owner = ID,
        space = Bar::LEN + 8
    )]
    bar_12: AccountLoader<'info, Bar>,
    #[account(
        init,
        payer = authority, owner = ID,
        space = Bar::LEN + 8
    )]
    bar_13: AccountLoader<'info, Bar>,
    #[account(
        init,
        payer = authority, owner = ID,
        space = Bar::LEN + 8
    )]
    bar_14: AccountLoader<'info, Bar>,
    #[account(
        init,
        payer = authority, owner = ID,
        space = Bar::LEN + 8
    )]
    bar_15: AccountLoader<'info, Bar>,
    #[account(
        init,
        payer = authority, owner = ID,
        space = Bar::LEN + 8
    )]
    bar_16: AccountLoader<'info, Bar>,
    #[account(
        init,
        payer = authority, owner = ID,
        space = Bar::LEN + 8
    )]
    bar_17: AccountLoader<'info, Bar>,
    #[account(
        init,
        payer = authority, owner = ID,
        space = Bar::LEN + 8
    )]
    bar_18: AccountLoader<'info, Bar>,
    #[account(
        init,
        payer = authority, owner = ID,
        space = Bar::LEN + 8
    )]
    bar_19: AccountLoader<'info, Bar>,
    #[account(
        init,
        payer = authority, owner = ID,
        space = Bar::LEN + 8
    )]
    bar_20: AccountLoader<'info, Bar>,
    #[account(mut)]
    authority: Signer<'info>,
    foo: AccountLoader<'info, Foo>,
    system_program: AccountInfo<'info>,
}**
**

Got good results ,
without closure : Error: Function _ZN95_$LT$zero_copy..CreateBar$u20$as$u20$anchor_lang..Accounts$LT$zero_copy..CreateBarBumps$GT$$GT$12try_accounts17hc3d5369b2e707256E Stack offset of **15208** exceeded max offset of **4096** by **11112** bytes, please minimize large stack variables
with closure : Error: Function _ZN95_$LT$zero_copy..CreateBar$u20$as$u20$anchor_lang..Accounts$LT$zero_copy..CreateBarBumps$GT$$GT$12try_accounts17ha6b9d25c9bc115a3E Stack offset of **4632** exceeded max offset of **4096** by **536** bytes, please minimize large stack variables
For now it seems the closures are acting like #[inline(never)] , but idk if this behavior will stay the same across rust versions, one cant at this point in time use attributes on closures so we cant add a the inline attribute explicitly on the closures , tracking rust rfc : rust-lang/rust#15701

@workingjubilee
Copy link

For now it seems the closures are acting like #[inline(never)] , but idk if this behavior will stay the same across rust versions, one cant at this point in time use attributes on closures so we cant add a the inline attribute explicitly on the closures , tracking rust rfc : rust-lang/rust#15701

hm? yeah you can.

@workingjubilee
Copy link

see rust-lang/rust#15701 (comment)

@nabeel99
Copy link
Contributor

Hey

For now it seems the closures are acting like #[inline(never)] , but idk if this behavior will stay the same across rust versions, one cant at this point in time use attributes on closures so we cant add a the inline attribute explicitly on the closures , tracking rust rfc : rust-lang/rust#15701

hm? yeah you can.

see rust-lang/rust#15701 (comment)

Seems i misread that pr and thought its still not avbl on stable-wrapping in curly braces compiles fine- thanks for the headsup @workingjubilee .

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants