Add Extend::{extend_one,extend_reserve} by cuviper · Pull Request #72162 · rust-lang/rust (original) (raw)

Are we using size hints already to pre-reserve?

There's no way to do that in partition and unzip as-is, since they only have Default + Extend to work with. So that new pre-allocation does play a big part in the performance gain.

I would've expected an extend(Some(foo)) to basically compile down to a push - if that doesn't happen today, perhaps worth investigating?

Here's a playground of the benchmarks from #72085, and my local results:

test bench_functional        ... bench:       1,292 ns/iter (+/- 31)
test bench_imperative_extend ... bench:         803 ns/iter (+/- 8)
test bench_imperative_push   ... bench:         410 ns/iter (+/- 6)

Note that functional is the status quo unzip, whereas this PR gets it down to push performance.

Looking at the assembly, the biggest difference I see is that extend(Some(_)) has an unconditional call to reserve, I believe from here, whereas push only calls that when it actually needs more capacity. Maybe we could branch like that in extend too, but I think that would pessimize the general case, compared to partition and unzip that are hammering extend in a loop.

That's particular to Vec -- other containers may have their own trade-offs. Note that I did make the default extend_one just call extend(Some(_)) anyway.

I think the only reason we have the somewhat awkward method names is also because Extend is im the prelude, right? So we don't want to use e.g. push and reserve?

Yeah, I think being in the prelude warrants caution in method ambiguity.