Skip to content

Vec::from_iter specialization is slower on TrustedLen iterators from thin-vec #160048

Description

@panstromek

In #159974, #159975, we found out that updating and enabling "unstable" feature flag in thin-vec makes some new-solver benchmarks slower.

I tried to measure this in isolation on the old version in #160010, where the old "unstable" flag only adds TrustedLen impl to thin_vec::IntoIter and thin_vec::Drain. The results for only adding the flag are here and they reproduce the regression: #160010 (comment)

Based on the cachegrind diffs, the culprit is mostly in this iterator chain:

fn collect_remaining_errors(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<E> {
self.obligations
.pending
.drain(..)
.map(|(obligation, _)| NextSolverError::Ambiguity(obligation))
.chain(
self.obligations
.overflowed
.drain(..)
.map(|obligation| NextSolverError::Overflow(obligation)),
)
.map(|e| E::from_solver_error(infcx, e))
.collect()
}

pending.drain() creates thin_vec::Drain iterator. The rest of it are iterators from std.

Adding no-op skip(0) before the collect() call disables the specialization (because Skip doesn't implement TrustedLen) and fixes most of the regression: #160010 (comment), but not all of it, so it looks like the problem is not specific to this chain.

If I understand the specialization resolution correctly, the problematic from_iter impl is this one:

fn from_iter(iterator: I) -> Self {
let mut vector = match iterator.size_hint() {
(_, Some(upper)) => Vec::with_capacity(upper),
// TrustedLen contract guarantees that `size_hint() == (_, None)` means that there
// are more than `usize::MAX` elements.
// Since the previous branch would eagerly panic if the capacity is too large
// (via `with_capacity`) we do the same here.
_ => panic!("capacity overflow"),
};
// reuse extend specialization for TrustedLen
vector.spec_extend(iterator);
vector
}

Note that #160005 optimizes some of the next-solver data structures, so it's possible the issue will no longer reproduce on this code on main as visibly, but the issue might still exist.

I currently don't have time to research this further and create smaller reproducer, but maybe somebody who has more experience with these specializations has a better idea for what's going on.

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-iteratorsArea: IteratorsA-specializationArea: Trait impl specializationI-slowIssue: Problems and improvements with respect to performance of generated code.needs-triageThis issue may need triage. Remove it if it has been sufficiently triaged.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions