Skip to content

Don't reserveCapacity in append(contentsOf:), it breaks API guarantees of asymptotic complexity #65927

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
May 19, 2023

Conversation

Catfish-Man
Copy link
Contributor

@Catfish-Man Catfish-Man commented May 15, 2023

Fixes rdar://109577273

…ion.append(contentsOf:) before falling back to a slow element-by-element loop. Fixes rdar://109059874
@Catfish-Man Catfish-Man self-assigned this May 15, 2023
@Catfish-Man
Copy link
Contributor Author

@swift-ci please benchmark

@Catfish-Man
Copy link
Contributor Author

@swift-ci please test

@lorentey
Copy link
Member

Note: This builds on #65778, and it includes the commits from there.

The new stuff is in 476c472.

Copy link
Member

@lorentey lorentey left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would probably be a good idea to add a test that ensures that none of the default RangeReplaceableCollection mutation implementations end up calling reserveCapacity, to prevent well-meaning changes that would reintroduce these from landing without breaking a test.

We may have a minimal RRC type in the stdlib already, but if not, we could add a dummy one directly in the test. (It could either count invocations like the existing minimal types, or it could just fatalError in reserveCapacity. 🤔)

Comment on lines 462 to 463
let approximateCapacity = self.count + newElements.underestimatedCount
self.reserveCapacity(approximateCapacity)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It may be a good idea to add an explicit comment about this gotcha, to help prevent this from happening again.

Suggested change
let approximateCapacity = self.count + newElements.underestimatedCount
self.reserveCapacity(approximateCapacity)
// Note: This used to call `reserveCapacity` here, but that can dramatically
// slow down clients that repeatedly call `append(contentsOf:)`. Do not
// re-add that. (The usual implementation of `reserveCapacity` for
// contiguous collections disables exponential growth for the backing store,
// ruining the amortized O(*m*) complexity that we promise above.)

@Catfish-Man
Copy link
Contributor Author

@swift-ci please test

@Catfish-Man Catfish-Man merged commit e342ac4 into swiftlang:main May 19, 2023
@Catfish-Man Catfish-Man deleted the no-reservations branch May 19, 2023 19:33
meg-gupta pushed a commit to meg-gupta/swift that referenced this pull request May 22, 2023
…s of asymptotic complexity (swiftlang#65927)

Don't reserveCapacity in append(contentsOf:), it breaks API guarantees of asymptotic complexity. Fixes rdar://109577273
NuriAmari pushed a commit to NuriAmari/swift that referenced this pull request May 28, 2023
…s of asymptotic complexity (swiftlang#65927)

Don't reserveCapacity in append(contentsOf:), it breaks API guarantees of asymptotic complexity. Fixes rdar://109577273
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants