Skip to content

ExactSizeIterator seems to generate worse assembly if mutated before collected into Vec #110734

@ZhennanWu

Description

@ZhennanWu

Godbolt link https://godbolt.org/z/cMdx6v1G9

pub fn exact_size(){
    let it = std::hint::black_box(vec![1,2,3].into_iter());
    let v = it.collect::<Vec<_>>();
    std::hint::black_box(v);
}


pub fn var_size(){
    let it = std::hint::black_box(vec![1,2,3].into_iter()).filter(|x| true);
    let v = it.collect::<Vec<_>>();
    std::hint::black_box(v);
}

I expected to see this happen: exact_size to generate better assembly than var_size

Instead, this happened:

  1. var_size generates way shorter assembly (~90) than exact size (>200) with zero call to allocate Vec
  2. It seems to me var_size is trying to allocate the Vec on stack. This optimization did not happen on exact_size (Excuse me if I misintepreted the assembly).

The key to trigger this deoptimization seems to be mutating the iterator before collecting. This example is a reduction of a real-world code where the first few elements are processed differently and the rest of the elements is collected into Vec then consumed locally.

Godbolt link for a more realistic example: https://godbolt.org/z/sccdTcvh6

#[inline(never)]
pub fn example<I: Iterator>(mut it: I){
    // .... read and process a few elements
    let Some(first_elem) = it.next() else {return;};
    // let mut it = it.filter(|x| true); // <------ This causes different optimization if ExactSizeIterator
    let v = it.collect::<Vec<_>>();
    for elem in v {
        // Different processing on rest of the elements.
    }
}

pub fn call_example() {
    example(vec![1,2,3].into_iter())
}

Edit: Include a more realistic example

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-codegenArea: Code generationC-bugCategory: This is a bug.C-optimizationCategory: An issue highlighting optimization opportunities or PRs implementing suchI-heavyIssue: Problems and improvements with respect to binary size of generated code.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions