Skip to content
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

Improve bulk stream teardown. #261

Merged
merged 2 commits into from
Nov 26, 2020

Conversation

Lukasa
Copy link
Contributor

@Lukasa Lukasa commented Nov 25, 2020

Motivation:

The logic for doing bulk stream teardown in the ConnectionStreamsState
was always a bit janky: we'd grab all the stream IDs out of the map,
filter out anything that was too small, insert them one-by-one into a
circular buffer, and then report back. I basically just translated that
directly into StreamMap, but on StreamMap this potentially performs very
poorly, as attempting to find the pivot point becomes a linear scan
through the stream data even when there are loads of streams.

This patch swaps over to add a specialized function for bulk-removal of
streams. The specialized function passes the slice of data to be removed
to the caller so that they can compute on it. It does this by a closure
to try to avoid a CoW where possible.

While we're here I tried to address the loop over the CircularBuffer of
recently reset streams. This flushed out an awkward bug that has existed
in our recently reset streams logic for a very long time, which is that
while we tried to maintain the size of the circular buffer, in
practice we misunderstood the CircularBuffer invariants and so this
never worked. I've added a bunch of unit tests for that logic and fixed
it up.

These two changes together will improve performance in stream bulk
teardown. This is an uncommon case (it only happens on GOAWAY), but it's
still good to make that faster.

Modifications:

  • New operation on StreamMap
  • New prependWithoutExpanding(contentsOf:) operation
  • Unit tests for the circular buffer and stream map logic.

Result:

Better performance on bulk stream teardown. Improves instructions executed in the stream teardown benchmark by 0.2%.

@Lukasa Lukasa added the 🔨 semver/patch No public API change. label Nov 25, 2020
@Lukasa Lukasa force-pushed the cb-more-efficient-teardown branch from 4d69e08 to 39ace2d Compare November 25, 2020 13:48
Copy link
Contributor

@glbrntt glbrntt left a comment

Choose a reason for hiding this comment

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

LGTM

let freeSpace = self.effectiveCapacity - self.count

if newElementCount >= self.effectiveCapacity {
// We need to completely replace the storage, and then only insert `self.capacity` elements.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
// We need to completely replace the storage, and then only insert `self.capacity` elements.
// We need to completely replace the storage, and then only insert `self.effectiveCapacity` elements.

}
}

// ?
Copy link
Contributor

Choose a reason for hiding this comment

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

Not a confidence inspiring comment!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hah! I left that for myself when I was unsure if the logic was quite right. I convinced myself it was.

Motivation:

The logic for doing bulk stream teardown in the ConnectionStreamsState
was always a bit janky: we'd grab all the stream IDs out of the map,
filter out anything that was too small, insert them one-by-one into a
circular buffer, and then report back. I basically just translated that
directly into StreamMap, but on StreamMap this potentially performs very
poorly, as attempting to find the pivot point becomes a linear scan
through the stream data even when there are _loads_ of streams.

This patch swaps over to add a specialized function for bulk-removal of
streams. The specialized function passes the slice of data to be removed
to the caller so that they can compute on it. It does this by a closure
to try to avoid a CoW where possible.

While we're here I tried to address the loop over the CircularBuffer of
recently reset streams. This flushed out an awkward bug that has existed
in our recently reset streams logic for a very long time, which is that
while we _tried_ to maintain the size of the circular buffer, in
practice we misunderstood the CircularBuffer invariants and so this
never worked. I've added a bunch of unit tests for that logic and fixed
it up.

These two changes together will improve performance in stream bulk
teardown. This is an uncommon case (it only happens on GOAWAY), but it's
still good to make that faster.

Modifications:

- New operation on StreamMap
- New prependWithoutExpanding(contentsOf:) operation
- Unit tests for the circular buffer and stream map logic.

Result:

Better performance on bulk stream teardown.
@Lukasa Lukasa force-pushed the cb-more-efficient-teardown branch from 39ace2d to 06a2cb7 Compare November 25, 2020 20:27
Copy link
Contributor

@PeterAdams-A PeterAdams-A left a comment

Choose a reason for hiding this comment

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

Looks good.

case .server:
let index = self.serverInitiated.findIndexForFirstStreamIDLargerThan(streamID)
block(self.serverInitiated[index...])
self.serverInitiated.removeSubrange(index...)
Copy link
Contributor

Choose a reason for hiding this comment

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

The duplication in places like this does look pretty sad.

@PeterAdams-A PeterAdams-A merged commit 57976bc into apple:main Nov 26, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🔨 semver/patch No public API change.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants