Avoid unnecessary allocation by way of tuple conversion. (#994)

Motivation:

Due to https://bugs.swift.org/browse/SR-10614, assigning an array containing a tuple type
to a variable that expects an array containing a different-but-compatible tuple type will cause
an allocation and copy of that array storage.

In some cases this is necessary, but we were doing it in the HTTPHeaders constructors, which meant
that the swift-nio-http2 code was hitting a hilariously over the top number of allocations.

Modifications:

- Change the internal storage type of HTTPHeaders to match the public constructor.

Result:

Fewer allocations when constructing HTTPHeaders
This commit is contained in:
Cory Benfield 2019-05-03 21:12:19 +01:00 committed by GitHub
parent 9e451149ca
commit 768c3ab328
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 20 additions and 5 deletions

View File

@ -19,7 +19,7 @@ token=$(create_token)
start_server "$token"
do_curl "$token" -H "foo: bar" --http1.0 \
"http://foobar.com/dynamic/info" > "$tmp/out"
if ! grep -q '(name: "foo", value: "bar")' "$tmp/out"; then
if ! grep -q '("foo", "bar")' "$tmp/out"; then
fail "couldn't find header in response"
fi
stop_server "$token"

View File

@ -58,7 +58,7 @@ cd ..
"$swift_bin" run -c release | tee "$tmp/output"
)
for test in 1000_reqs_1_conn 1_reqs_1000_conn ping_pong_1000_reqs_1_conn bytebuffer_lots_of_rw future_lots_of_callbacks scheduling_10000_executions; do
for test in 1000_reqs_1_conn 1_reqs_1000_conn ping_pong_1000_reqs_1_conn bytebuffer_lots_of_rw future_lots_of_callbacks scheduling_10000_executions creating_10000_headers; do
cat "$tmp/output" # helps debugging
total_allocations=$(grep "^$test.total_allocations:" "$tmp/output" | cut -d: -f2 | sed 's/ //g')
not_freed_allocations=$(grep "^$test.remaining_allocations:" "$tmp/output" | cut -d: -f2 | sed 's/ //g')

View File

@ -452,5 +452,16 @@ public func swiftMain() -> Int {
return 10_000
}
measureAndPrint(desc: "creating_10000_headers") {
var count = 0
for i in 0..<10_000 {
let baseHeaders: [(String, String)] = [("Host", "example.com"), ("Content-Length", "4")]
count += HTTPHeaders(baseHeaders).count
}
return count
}
return 0
}

View File

@ -278,7 +278,7 @@ extension HTTPHeaders {
/// or split representation, such that header fields that are able to be repeated
/// can be represented appropriately.
public struct HTTPHeaders: CustomStringConvertible, ExpressibleByDictionaryLiteral {
internal var headers: [(name: String, value: String)]
internal var headers: [(String, String)]
internal var keepAliveState: KeepAliveState = .unknown
public var description: String {
@ -289,7 +289,7 @@ public struct HTTPHeaders: CustomStringConvertible, ExpressibleByDictionaryLiter
return self.headers.map { $0.0 }
}
internal init(_ headers: [Element], keepAliveState: KeepAliveState) {
internal init(_ headers: [(String, String)], keepAliveState: KeepAliveState) {
self.headers = headers
self.keepAliveState = keepAliveState
}
@ -452,7 +452,7 @@ extension HTTPHeaders: RandomAccessCollection {
public typealias Element = (name: String, value: String)
public struct Index: Comparable {
fileprivate let base: Array<HTTPHeaders>.Index
fileprivate let base: Array<(String, String)>.Index
public static func < (lhs: Index, rhs: Index) -> Bool {
return lhs.base < rhs.base
}

View File

@ -24,6 +24,7 @@ services:
- MAX_ALLOCS_ALLOWED_ping_pong_1000_reqs_1_conn=4600
- MAX_ALLOCS_ALLOWED_bytebuffer_lots_of_rw=2100
- MAX_ALLOCS_ALLOWED_future_lots_of_callbacks=99100
- MAX_ALLOCS_ALLOWED_creating_10000_headers=10100
test:
image: swift-nio:16.04-5.1
@ -34,6 +35,7 @@ services:
- MAX_ALLOCS_ALLOWED_ping_pong_1000_reqs_1_conn=4600
- MAX_ALLOCS_ALLOWED_bytebuffer_lots_of_rw=2100
- MAX_ALLOCS_ALLOWED_future_lots_of_callbacks=99100
- MAX_ALLOCS_ALLOWED_creating_10000_headers=10100
echo:
image: swift-nio:16.04-5.1

View File

@ -23,6 +23,7 @@ services:
- MAX_ALLOCS_ALLOWED_bytebuffer_lots_of_rw=2100
- MAX_ALLOCS_ALLOWED_future_lots_of_callbacks=99100
- MAX_ALLOCS_ALLOWED_scheduling_10000_executions=20150
- MAX_ALLOCS_ALLOWED_creating_10000_headers=10100
test:
image: swift-nio:18.04-5.0
@ -34,6 +35,7 @@ services:
- MAX_ALLOCS_ALLOWED_bytebuffer_lots_of_rw=2100
- MAX_ALLOCS_ALLOWED_future_lots_of_callbacks=99100
- MAX_ALLOCS_ALLOWED_scheduling_10000_executions=20150
- MAX_ALLOCS_ALLOWED_creating_10000_headers=10100
echo:
image: swift-nio:18.04-5.0