-
Notifications
You must be signed in to change notification settings - Fork 84
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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.
- Loading branch information
Showing
10 changed files
with
372 additions
and
19 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
44 changes: 44 additions & 0 deletions
44
Tests/NIOHTTP2Tests/CircularBufferExtensionsTests+XCTest.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// This source file is part of the SwiftNIO open source project | ||
// | ||
// Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors | ||
// Licensed under Apache License v2.0 | ||
// | ||
// See LICENSE.txt for license information | ||
// See CONTRIBUTORS.txt for the list of SwiftNIO project authors | ||
// | ||
// SPDX-License-Identifier: Apache-2.0 | ||
// | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// CircularBufferExtensionsTests+XCTest.swift | ||
// | ||
import XCTest | ||
|
||
/// | ||
/// NOTE: This file was generated by generate_linux_tests.rb | ||
/// | ||
/// Do NOT edit this file directly as it will be regenerated automatically when needed. | ||
/// | ||
|
||
extension CircularBufferExtensionsTests { | ||
|
||
@available(*, deprecated, message: "not actually deprecated. Just deprecated to allow deprecated tests (which test deprecated functionality) without warnings") | ||
static var allTests : [(String, (CircularBufferExtensionsTests) -> () throws -> Void)] { | ||
return [ | ||
("testPrependFromEmpty", testPrependFromEmpty), | ||
("testPrependWithSpace", testPrependWithSpace), | ||
("testPrependWithExactlyEnoughSpace", testPrependWithExactlyEnoughSpace), | ||
("testPrependWithoutEnoughSpace", testPrependWithoutEnoughSpace), | ||
("testPrependContentsOfWithLotsOfSpace", testPrependContentsOfWithLotsOfSpace), | ||
("testPrependContentsOfWithExactlyTheSpace", testPrependContentsOfWithExactlyTheSpace), | ||
("testPrependContentsOfWithEnoughSpaceIfWeRemoveAnElement", testPrependContentsOfWithEnoughSpaceIfWeRemoveAnElement), | ||
("testPrependContentsOfWithEnoughSpaceIfWeRemoveEverything", testPrependContentsOfWithEnoughSpaceIfWeRemoveEverything), | ||
("testPrependContentsOfWithoutEnoughSpaceButContainingElements", testPrependContentsOfWithoutEnoughSpaceButContainingElements), | ||
("testPrependContentsOfWithExactlyTheSpaceFromEmpty", testPrependContentsOfWithExactlyTheSpaceFromEmpty), | ||
("testPrependContentsOfWithoutEnoughSpaceFromEmpty", testPrependContentsOfWithoutEnoughSpaceFromEmpty), | ||
] | ||
} | ||
} | ||
|
140 changes: 140 additions & 0 deletions
140
Tests/NIOHTTP2Tests/CircularBufferExtensionsTests.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// This source file is part of the SwiftNIO open source project | ||
// | ||
// Copyright (c) 2020 Apple Inc. and the SwiftNIO project authors | ||
// Licensed under Apache License v2.0 | ||
// | ||
// See LICENSE.txt for license information | ||
// See CONTRIBUTORS.txt for the list of SwiftNIO project authors | ||
// | ||
// SPDX-License-Identifier: Apache-2.0 | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
import XCTest | ||
import NIO | ||
@testable import NIOHTTP2 | ||
|
||
final class CircularBufferExtensionsTests: XCTestCase { | ||
func testPrependFromEmpty() { | ||
var buffer = CircularBuffer<Int>(initialCapacity: 16) | ||
buffer.prependWithoutExpanding(1) | ||
XCTAssertEqual(Array(buffer), [1]) | ||
XCTAssertEqual(buffer.count, 1) | ||
XCTAssertEqual(buffer.effectiveCapacity, 15) | ||
} | ||
|
||
func testPrependWithSpace() { | ||
var buffer = CircularBuffer<Int>(initialCapacity: 16) | ||
buffer.append(contentsOf: [1, 2, 3]) | ||
buffer.prependWithoutExpanding(4) | ||
XCTAssertEqual(Array(buffer), [4, 1, 2, 3]) | ||
XCTAssertEqual(buffer.count, 4) | ||
XCTAssertEqual(buffer.effectiveCapacity, 15) | ||
} | ||
|
||
func testPrependWithExactlyEnoughSpace() { | ||
var buffer = CircularBuffer<Int>(initialCapacity: 16) | ||
buffer.append(contentsOf: 1..<15) | ||
buffer.prependWithoutExpanding(15) | ||
XCTAssertEqual(Array(buffer), [15, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]) | ||
XCTAssertEqual(buffer.count, 15) | ||
XCTAssertEqual(buffer.effectiveCapacity, 15) | ||
} | ||
|
||
func testPrependWithoutEnoughSpace() { | ||
var buffer = CircularBuffer<Int>(initialCapacity: 16) | ||
buffer.append(contentsOf: 1..<16) | ||
XCTAssertEqual(Array(buffer), [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]) | ||
XCTAssertEqual(buffer.count, 15) | ||
XCTAssertEqual(buffer.effectiveCapacity, 15) | ||
|
||
|
||
buffer.prependWithoutExpanding(17) | ||
XCTAssertEqual(Array(buffer), [17, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]) | ||
XCTAssertEqual(buffer.count, 15) | ||
XCTAssertEqual(buffer.effectiveCapacity, 15) | ||
} | ||
|
||
func testPrependContentsOfWithLotsOfSpace() { | ||
var buffer = CircularBuffer<Int>(initialCapacity: 16) | ||
buffer.append(contentsOf: [1, 2, 3]) | ||
XCTAssertEqual(Array(buffer), [1, 2, 3]) | ||
XCTAssertEqual(buffer.count, 3) | ||
XCTAssertEqual(buffer.effectiveCapacity, 15) | ||
|
||
buffer.prependWithoutExpanding(contentsOf: [4, 5, 6]) | ||
XCTAssertEqual(Array(buffer), [6, 5, 4, 1, 2, 3]) | ||
XCTAssertEqual(buffer.count, 6) | ||
XCTAssertEqual(buffer.effectiveCapacity, 15) | ||
} | ||
|
||
func testPrependContentsOfWithExactlyTheSpace() { | ||
var buffer = CircularBuffer<Int>(initialCapacity: 16) | ||
buffer.append(contentsOf: [1, 2, 3]) | ||
XCTAssertEqual(Array(buffer), [1, 2, 3]) | ||
XCTAssertEqual(buffer.count, 3) | ||
XCTAssertEqual(buffer.effectiveCapacity, 15) | ||
|
||
buffer.prependWithoutExpanding(contentsOf: Array(4..<16)) | ||
XCTAssertEqual(Array(buffer), [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 1, 2, 3]) | ||
XCTAssertEqual(buffer.count, 15) | ||
XCTAssertEqual(buffer.effectiveCapacity, 15) | ||
} | ||
|
||
func testPrependContentsOfWithEnoughSpaceIfWeRemoveAnElement() { | ||
var buffer = CircularBuffer<Int>(initialCapacity: 16) | ||
buffer.append(contentsOf: [1, 2, 3]) | ||
XCTAssertEqual(Array(buffer), [1, 2, 3]) | ||
XCTAssertEqual(buffer.count, 3) | ||
XCTAssertEqual(buffer.effectiveCapacity, 15) | ||
|
||
buffer.prependWithoutExpanding(contentsOf: Array(4..<17)) | ||
XCTAssertEqual(Array(buffer), [16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 1, 2]) | ||
XCTAssertEqual(buffer.count, 15) | ||
XCTAssertEqual(buffer.effectiveCapacity, 15) | ||
} | ||
|
||
func testPrependContentsOfWithEnoughSpaceIfWeRemoveEverything() { | ||
var buffer = CircularBuffer<Int>(initialCapacity: 16) | ||
buffer.append(contentsOf: [1, 2, 3]) | ||
XCTAssertEqual(Array(buffer), [1, 2, 3]) | ||
XCTAssertEqual(buffer.count, 3) | ||
XCTAssertEqual(buffer.effectiveCapacity, 15) | ||
|
||
buffer.prependWithoutExpanding(contentsOf: Array(4..<19)) | ||
XCTAssertEqual(Array(buffer), [18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4]) | ||
XCTAssertEqual(buffer.count, 15) | ||
XCTAssertEqual(buffer.effectiveCapacity, 15) | ||
} | ||
|
||
func testPrependContentsOfWithoutEnoughSpaceButContainingElements() { | ||
var buffer = CircularBuffer<Int>(initialCapacity: 16) | ||
buffer.append(contentsOf: [1, 2, 3]) | ||
XCTAssertEqual(Array(buffer), [1, 2, 3]) | ||
XCTAssertEqual(buffer.count, 3) | ||
XCTAssertEqual(buffer.effectiveCapacity, 15) | ||
|
||
buffer.prependWithoutExpanding(contentsOf: Array(4..<32)) | ||
XCTAssertEqual(Array(buffer), [31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17]) | ||
XCTAssertEqual(buffer.count, 15) | ||
XCTAssertEqual(buffer.effectiveCapacity, 15) | ||
} | ||
|
||
func testPrependContentsOfWithExactlyTheSpaceFromEmpty() { | ||
var buffer = CircularBuffer<Int>(initialCapacity: 16) | ||
buffer.prependWithoutExpanding(contentsOf: Array(1..<16)) | ||
XCTAssertEqual(Array(buffer), [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1]) | ||
XCTAssertEqual(buffer.count, 15) | ||
XCTAssertEqual(buffer.effectiveCapacity, 15) | ||
} | ||
|
||
func testPrependContentsOfWithoutEnoughSpaceFromEmpty() { | ||
var buffer = CircularBuffer<Int>(initialCapacity: 16) | ||
buffer.prependWithoutExpanding(contentsOf: Array(1..<32)) | ||
XCTAssertEqual(Array(buffer), [31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17]) | ||
XCTAssertEqual(buffer.count, 15) | ||
XCTAssertEqual(buffer.effectiveCapacity, 15) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.