-
Notifications
You must be signed in to change notification settings - Fork 656
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 HTTPHeaders description performance #3063
Conversation
Motivation: As outlined in this [issue](apple#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.
I think we need a benchmark here to get an idea of whether this is actually faster. |
Sure, I'm happy to add benchmarks to confirm this. @weissi Would you mind sharing with me how did you generate the images from the original issue? I tried to reach out to you on X/Twitter. Thanks. |
Oh sorry, I'm mostly on Bluesky these days. If you're on a Mac, the easiest thing is probably to write a quick harness that does
then compile that in release mode and run it with To see the Flame Graphs on Mac, using Instruments is easiest. |
And ideally, you'd add the benchmark to the NIOPerformanceTester target too. |
Thank you @weissi! I did some tests and here is what I found. Consider the following harness: @_optimize(none) @inline(never)
func go() {
let headers = Array(repeating: ("Test", "Test"), count: 100)
for _ in 1...1_000 {
let str = headers.description
precondition(str.utf8.count > 100)
}
}
go() After compiling it with :
and running
Now, let's consider the harness with which replaces the call to @_optimize(none) @inline(never)
func go() {
let headers = Array(repeating: ("Test", "Test"), count: 100)
for _ in 0...1_000 {
let str = headers.lazy.map {
"\($0.0): \($0.1)"
}
.joined(separator: "; ")
precondition(str.utf8.count > 100)
}
}
go() We can copmpile it with the same flags and run
We can see that we have a roughly 8x increase in speed. I also added a benchmark to the Last, but not least, the flame graphs 🔥. |
Super cool, thank you!! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shiny, this is fantastic. Thanks so much @supersonicbyte! ✨ I'll kick off CI and see what it thinks.
Ok, so somewhat unsurprisingly we've improved our allocation count numbers, which causes the integration tests to fail (so that we can revise the thresholds downwards). If you're on macOS, the solution for updating these is to open the CI jobs one-by-one, copy the output, and then run |
Head branch was pushed to by a user without write access
Hey @Lukasa, I don't believe that is the case actually. I have run the suggested command but it didn't generate any diff from current main. Then I dig in deeper and found out actually that the reason of the failing is |
Oh nice, good catch, thanks! |
Motivation:
As outlined in this issue by @weissi, the current performance of calling
description
onHTTPHeaders
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 intodescription
of an Array.Result:
A more performant implementation of
HTTPHeaders
description.