Skip to content

Vec::retain() is significantly slower than into_iter().filter().collect() #91497

Closed
@kageru

Description

@kageru

I noticed today that using Vec::retain is much slower than filtering and allocating a new Vector.
I realize the former is probably more memory efficient, but I still found it surprising that it would be that much slower (or really slower at all).

When testing with this code:

#![feature(test)]
extern crate test;

fn main() {
    let xs: Vec<i32> = (0..1000).collect();
    assert_eq!(even_with_retain(xs.clone()), even_with_filter(xs.clone()));
}

pub fn even_with_retain(mut xs: Vec<i32>) -> Vec<i32> {
    xs.retain(|x| x & 1 == 0);
    xs
}

pub fn even_with_filter(xs: Vec<i32>) -> Vec<i32> {
    xs.into_iter().filter(|x| x & 1 == 0).collect()
}

#[bench]
fn bench_retain(b: &mut test::Bencher) {
    let xs: Vec<i32> = (0..1000).collect();
    b.iter(|| assert_eq!(even_with_retain(test::black_box(xs.clone())).len(), 500));
}

#[bench]
fn bench_filter_collect(b: &mut test::Bencher) {
    let xs: Vec<i32> = (0..1000).collect();
    b.iter(|| assert_eq!(even_with_filter(test::black_box(xs.clone())).len(), 500));
}

on 1.59.0-nightly (48a5999 2021-12-01), I get these benchmark results:

test bench_filter_collect ... bench:         383 ns/iter (+/- 4)
test bench_retain         ... bench:       1,891 ns/iter (+/- 17)

on a Ryzen 5900X running Linux. Testing on a different machine (Xeon E3-1271 v3), I get similar numbers:

test bench_filter_collect ... bench:         498 ns/iter (+/- 29)
test bench_retain         ... bench:       1,800 ns/iter (+/- 44)

Vec::retain seemed like the obvious choice to me, so it being slower is either a bug or should be documented somewhere.

Godbolt

Metadata

Metadata

Assignees

Labels

A-codegenArea: Code generationA-collectionsArea: `std::collections`A-iteratorsArea: IteratorsC-bugCategory: This is a bug.I-slowIssue: Problems and improvements with respect to performance of generated code.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.T-libsRelevant to the library 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