Skip to content

Commit

Permalink
Improve HTTPHeaders description performance (#3063)
Browse files Browse the repository at this point in the history
### Motivation:

As outlined in this
[issue](#2930) by @weissi, the
current performance of calling `description` on `HTTPHeaders` is
undermined by the dynamism of Array.

### Modifications:

The proposed solution replaces the implementation of `description` to
iterate over the items and print them out manually. This provides a
faster solution since we bypass the cost of calling into `description`
of an Array.


### Result:

A more performant implementation of `HTTPHeaders` description.

---------

Co-authored-by: Johannes Weiss <[email protected]>
Co-authored-by: Cory Benfield <[email protected]>
  • Loading branch information
3 people authored Jan 20, 2025
1 parent fd6eb16 commit bf41ad1
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 3 deletions.
4 changes: 2 additions & 2 deletions IntegrationTests/tests_01_http/test_07_headers_work.sh
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ source defines.sh
token=$(create_token)
start_server "$token"
do_curl "$token" -H "foo: bar" --http1.0 \
"http://foobar.com/dynamic/info" > "${tmp:?"tmp variable not set"}/out"
if ! grep -q '("foo", "bar")' "$tmp/out"; then
"http://foobar.com/dynamic/info" >"${tmp:?"tmp variable not set"}/out"
if ! grep -q 'foo: bar' "$tmp/out"; then
fail "couldn't find header in response"
fi
stop_server "$token"
5 changes: 4 additions & 1 deletion Sources/NIOHTTP1/HTTPTypes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,10 @@ public struct HTTPHeaders: CustomStringConvertible, ExpressibleByDictionaryLiter
internal var keepAliveState: KeepAliveState = .unknown

public var description: String {
self.headers.description
self.headers.lazy.map {
"\($0.0): \($0.1)"
}
.joined(separator: "; ")
}

internal var names: [String] {
Expand Down
11 changes: 11 additions & 0 deletions Sources/NIOPerformanceTester/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,17 @@ measureAndPrint(desc: "http_headers_canonical_form_trimming_whitespace_from_long
return count
}

measureAndPrint(desc: "http_headers_description_100k") {
let headers = HTTPHeaders(Array(repeating: ("String", "String"), count: 100))

for _ in 0..<100_000 {
let str = headers.description
precondition(str.utf8.count > 100)
}

return 0
}

measureAndPrint(desc: "bytebuffer_write_12MB_short_string_literals") {
let bufferSize = 12 * 1024 * 1024
var buffer = ByteBufferAllocator().buffer(capacity: bufferSize)
Expand Down
22 changes: 22 additions & 0 deletions Tests/NIOHTTP1Tests/HTTPHeadersTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -400,4 +400,26 @@ class HTTPHeadersTest: XCTestCase {
headers.reserveCapacity(4)
XCTAssertEqual(headers.capacity, 4)
}

func testHTTPHeadersDescription() {
let originalHeaders = [
("User-Agent", "1"),
("host", "2"),
("X-SOMETHING", "3"),
("SET-COOKIE", "foo=bar"),
("Set-Cookie", "buz=cux"),
]

let headers = HTTPHeaders(originalHeaders)

let expectedOutput = """
User-Agent: 1; \
host: 2; \
X-SOMETHING: 3; \
SET-COOKIE: foo=bar; \
Set-Cookie: buz=cux
"""

XCTAssertEqual(expectedOutput, headers.description)
}
}

0 comments on commit bf41ad1

Please sign in to comment.