forked from apple/swift-nio-http2
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Minimise channelReadComplete traffic. (apple#136)
Motivation: Currently swift-nio-http2 is extremely naive with its use of channelReadComplete calls in HTTP2StreamChannel, firing one channelReadComplete call per frame read. In high frame load cases this leads to a lot of excessive channelReadComplete traffic, which can cause unnecessary time spent in flush calls. We should try to minimise the amount of time we spend on this bookkeeping and save the CPU cost. Modifications: - Store a linked-list of HTTP2StreamChannels that need to have channelReadComplete fired on them. - Fire channelReadComplete based on this linked list. Result: Moderate improvement in performance in high-throughput workloads.
- Loading branch information
Showing
5 changed files
with
257 additions
and
5 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// This source file is part of the SwiftNIO open source project | ||
// | ||
// Copyright (c) 2019 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 | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
|
||
/// A linked list for storing HTTP2StreamChannels. | ||
/// | ||
/// Note that while this object *could* conform to `Sequence`, there is minimal value in doing | ||
/// that here, as it's so single-use. If we find ourselves needing to expand on this data type | ||
/// in future we can revisit that idea. | ||
struct StreamChannelList { | ||
private var head: HTTP2StreamChannel? | ||
private var tail: HTTP2StreamChannel? | ||
} | ||
|
||
/// A node for objects stored in an intrusive linked list. | ||
/// | ||
/// Any object that wishes to be stored in a linked list must embed one of these nodes. | ||
struct StreamChannelListNode { | ||
fileprivate enum ListState { | ||
case inList(next: HTTP2StreamChannel?) | ||
case notInList | ||
} | ||
|
||
fileprivate var state: ListState = .notInList | ||
|
||
internal init() { } | ||
} | ||
|
||
|
||
extension StreamChannelList { | ||
/// Append an element to the linked list. | ||
mutating func append(_ element: HTTP2StreamChannel) { | ||
precondition(!element.inList) | ||
|
||
guard case .notInList = element.streamChannelListNode.state else { | ||
preconditionFailure("Appended an element already in a list") | ||
} | ||
|
||
element.streamChannelListNode.state = .inList(next: nil) | ||
|
||
if let tail = self.tail { | ||
tail.streamChannelListNode.state = .inList(next: element) | ||
self.tail = element | ||
} else { | ||
assert(self.head == nil) | ||
self.head = element | ||
self.tail = element | ||
} | ||
} | ||
|
||
mutating func removeFirst() -> HTTP2StreamChannel? { | ||
guard let head = self.head else { | ||
assert(self.tail == nil) | ||
return nil | ||
} | ||
|
||
guard case .inList(let next) = head.streamChannelListNode.state else { | ||
preconditionFailure("Popped an element not in a list") | ||
} | ||
|
||
self.head = next | ||
if self.head == nil { | ||
assert(self.tail === head) | ||
self.tail = nil | ||
} | ||
|
||
head.streamChannelListNode = .init() | ||
return head | ||
} | ||
|
||
mutating func removeAll() { | ||
while self.removeFirst() != nil { } | ||
} | ||
} | ||
|
||
|
||
// MARK:- IntrusiveLinkedListElement helpers. | ||
extension HTTP2StreamChannel { | ||
/// Whether this element is currently in a list. | ||
internal var inList: Bool { | ||
switch self.streamChannelListNode.state { | ||
case .inList: | ||
return true | ||
case .notInList: | ||
return false | ||
} | ||
} | ||
} |
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