-
Notifications
You must be signed in to change notification settings - Fork 10.3k
Reuse previous materialized strings #8374
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
Conversation
9ba21aa
to
581d65b
Compare
src/Servers/Kestrel/Core/src/Internal/Http/HttpRequestHeaders.cs
Outdated
Show resolved
Hide resolved
src/Servers/Kestrel/Core/ref/Microsoft.AspNetCore.Server.Kestrel.Core.netcoreapp3.0.cs
Outdated
Show resolved
Hide resolved
6bb0e81
to
c4ae5a4
Compare
Using a larger set of headers to represent a browser request
And a 2 minute run
Before
28GB of headers are allocated After
0.3MB of headers are allocated 25M requests in 2 minutes is probably quite a high load though 😄 |
src/Servers/Kestrel/Core/src/Internal/Http/HttpRequestHeaders.cs
Outdated
Show resolved
Hide resolved
src/Servers/Kestrel/Core/src/Internal/Infrastructure/StringUtilities.cs
Outdated
Show resolved
Hide resolved
src/Servers/Kestrel/Core/src/Internal/Infrastructure/StringUtilities.cs
Outdated
Show resolved
Hide resolved
Updated perf test results
|
Now faster than current when not reusing headers, and faster still when reusing headers
|
Any chance someone with less than altruistic intent could alter something like the "Accept" header value for everyone with some unsafe antics? |
Sort of, it would only change it for the request (the values are only reused on a single connection); and the next request wouldn't match via content equality so it would generate a new one; but then you could change it again. However; that's a lot of work, since you can happily just replace the value in the headers collection without unsafe. |
53495b4
to
2cead3c
Compare
Ah OK, I was missing the part where the string is only reused for a connection, not across the application. |
Also avoids slow down from contention 😉 |
d130f47
to
115a26a
Compare
115a26a
to
f6674f3
Compare
|
|
03676cd
to
22c3c6a
Compare
Would it be more productive / less dangerous to focus on the HTTP/2 HPACK scenario where this is a first class protocol concept? Or is that already performing well? |
Sounds like we need a little more review here and can't squeeze it in to preview 4. Moving to preview 5. Let's try to close this out in the next day or so though (modulo any delays due to branching). |
|
||
private static bool GetDisableHeaderReuseSwitch() | ||
{ | ||
if (AppContext.TryGetSwitch(DisableHeaderReuseSwitch, out var disableHeaderReuse)) |
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.
Is there a switch to disable the similar changes to the target parsing?
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.
No. I saw it more as a paranoia setting #8374 (comment) as the headers could contain the cookies/auth info (whereas you shouldn't be putting this in the path).
Also the alternate path for startline would be quite messy. Can add it though?
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.
Is it really that messy? It is a little bit of paranoia, but I think having a switch that disable all reuse of previous materialize strings would relieve some of that paranoia.
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.
When I was originally thinking about how to do it; I thought it would be two branches with the two forms of parsing.
However, can just add it as first term in the if
tests; so no it wouldn't be messy.
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.
@benaadams we were discussion this and think it should be a regular option with, not an app context switch. It should just be a single option that can also be read from configuration (like we do for a bunch of other settings in kestrel)
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.
[Nevermind, I was confused which one you were referring to.]
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.
Wondering if Options can be "baked" to readonly static
s currently
e.g.
if (buffer.Length >= ServerOptions.Limits.MaxRequestLineSize)
is 3 indirections
G_M62071_IG06:
488B9780000000 mov rdx, gword ptr [rdi+128]
488B5218 mov rdx, gword ptr [rdx+24]
8B5220 mov edx, dword ptr [rdx+32]
4C63CA movsxd r9, edx
493BC9 cmp rcx, r9
7C37 jl SHORT G_M62072_IG07
if it can be "baked" then it would just be a const compare
G_M62071_IG06:
493BC9 cmp rcx, 8192
7C37 jl SHORT G_M62072_IG07
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.
Probably not as it would cause a problem if you were running more than once Kestrel instance in a process :-/
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.
Squished the api commits; just in case want it reverted b822a0f
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.
A little difference; but not huge
Config
Method | Mean | Op/s | Scaled | Allocated |
--------------------- |---------:|------------:|-------:|----------:|
PlaintextTechEmpower | 541.7 ns | 1,846,001.2 | 1.00 | 0 B |
LiveAspNet | 891.1 ns | 1,122,221.7 | 1.65 | 0 B |
Switch
Method | Mean | Op/s | Scaled | Allocated |
--------------------- |---------:|------------:|-------:|----------:|
PlaintextTechEmpower | 536.9 ns | 1,862,399.7 | 1.00 | 0 B |
LiveAspNet | 878.1 ns | 1,138,780.0 | 1.64 | 0 B |
@@ -196,6 +93,8 @@ public static string GeneratedFile() | |||
"TE", | |||
"Translate", | |||
"User-Agent", | |||
"DNT", | |||
"Upgrade-Insecure-Requests" |
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.
👍 Any other headers we should consider adding to the common headers list while we're thinking about it? @Tratcher
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.
I'd like to add a few more from https://github.com/aspnet/AspNetCore/blob/master/src/Http/Headers/src/HeaderNames.cs including the http/2 ones in that list of :authority
,:method
,:path
, etc
Which was also part of my motivation for halving the size of StringValues
dotnet/extensions#1283; so adding more headers wouldn't be too egregious.
However, was also looking to do that in a follow up, rather than this PR
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.
Makes sense. We should probably add the http/2 headers separately.
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.
I would like OnOriginFormTarget and OnAbsoluteTarget to be consistent regarding whether it explicitly resets QueryString to _parsedQueryString. I don't think it matters whether they do or don't, but they should be the same.
Co-Authored-By: benaadams <[email protected]>
@davidfowl feedback addressed? b822a0f |
0aab149
to
b822a0f
Compare
Thanks @benaadams! |
For each request from the browser a lot of repeated headers are sent that are identical to the previous request. This results in a lot of string allocations, especially for
Accept
,Cookie
andUser-Agent
when materializing these values.This change drops the sustained allocations to zero bytes/s for middleware with Json TE headers (e.g. 320MB less per 1M requests); will also drop header string allocation for MVC etc by the same amount.
Which fits with the goals of aspnet applications being able to run under lower memory conditions (See: "Proposal for .NET Core GC Support for Docker Limits")
This PR introduces
DisableStringReuse
which wentfalse
to reduces allocations for repeated request headers.Pre
Post
This change drops ~320MB of allocations for ~1M requests of non-pipelined Json TE benchmark
Addresses #8372