Skip to content

Performance pitfall and regression around closures in Rust 2021 #89470

Open
@CryZe

Description

@CryZe

In Rust 2021 the borrow checker got extended to see through borrow paths when writing closures. I've been wondering how this is implemented, but it seems like it's basically just syntax sugar for individual manual borrows that get moved in. This however leads to worse codegen than when a move closure would've been possible. So you might end up with code that performs worse by accident:

Godbolt

pub struct Foo {
    x: f32,
    y: f32,
    z: f32,
}

pub fn with_move(bar: &mut Foo) -> impl Fn() -> f32 + '_ {
    move || bar.x + bar.y + bar.z
}

pub fn without_move(bar: &mut Foo) -> impl Fn() -> f32 + '_ {
    || bar.x + bar.y + bar.z
}
example::with_move:
        mov     rax, rdi
        ret

example::without_move:
        mov     rax, rdi
        lea     rcx, [rsi + 4]
        mov     qword ptr [rdi], rsi
        add     rsi, 8
        mov     qword ptr [rdi + 8], rcx
        mov     qword ptr [rdi + 16], rsi
        ret

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-closuresArea: Closures (`|…| { … }`)A-edition-2021Area: The 2021 editionC-bugCategory: This is a bug.I-slowIssue: Problems and improvements with respect to performance of generated code.P-mediumMedium priority

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions