coral-xyz / anchor

⚓ Solana Sealevel Framework
https://anchor-lang.com
Apache License 2.0
3.36k stars 1.25k forks source link

Use closure for init constraints #2939

Open CanardMandarin opened 2 months ago

CanardMandarin commented 2 months ago

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?

vercel[bot] commented 2 months ago

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

A member of the Team first needs to authorize it.

nabeel99 commented 2 months ago

@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 commented 2 months ago

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 : https://github.com/rust-lang/rust/issues/15701

workingjubilee commented 1 month ago

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 : https://github.com/rust-lang/rust/issues/15701

hm? yeah you can.

workingjubilee commented 1 month ago

see https://github.com/rust-lang/rust/issues/15701#issuecomment-2073675764

nabeel99 commented 1 month ago

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 .