Skip to content
This repository was archived by the owner on Dec 18, 2018. It is now read-only.

Reduce lifetime of header values #608

Merged
merged 4 commits into from
Feb 18, 2016
Merged

Conversation

benaadams
Copy link
Contributor

For common header values (non-dictionary backed ones)

  • IHttpComponentFactory controls header and stream creation, disposal
  • Headers are attached at request start and detached at request end
  • Streams are attached at message body start and detached at request end
  • HttpComponentFactory implementation pools the headers, and streams
    if kestrel.maxPooledStreams > 0 || kestrel.maxPooledHeaders > 0
  • Request header values are nulled before detach (User input could be of indeterminate length)
  • If connection errors in-use headers & streams are not returned to pool

Faster cycling of header objects should cause the Response header values to remain in Gen0 as they are likely to be reset to a new value on each request.

Request values are reset if they have been set

Streams are uninitialized and reinitialized with frame reference so as not to prolong its lifetime.

Also doesn't hold onto header collections or stream objects while the Frame is in a keep-alive wait.

Resolves #607
Resolves #628
Resolves #629 when set > 0

Before:
headers

With this change, setting

{
  "kestrel.maxPooledStreams": 128,
  "kestrel.maxPooledHeaders": 128
}

After memory is down by 1.4GB, headers have moved out of the top spots
headers
And instead appear quite far down
headers


public void ResumeStreams()
{
_frameStreams.ResponseBody.ResumeAcceptingWrites();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: this is ordered differently than Pause and Stop :) (Response/Request here, vs Request/Response on the others.)

@muratg muratg added this to the 1.0.0-rc2 milestone Feb 4, 2016
if (((_bits & 1099511627776L) != 0)) _UserAgent = default(StringValues);
if (((_bits & 2199023255552L) != 0)) _Origin = default(StringValues);
if (((_bits & 4398046511104L) != 0)) _AccessControlRequestMethod = default(StringValues);
if (((_bits & 8796093022208L) != 0)) _AccessControlRequestHeaders = default(StringValues);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any evidence this is faster than simply resetting all the fields without checking which bits are set like we did before?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could do it in one statement by putting them in a struct and doing _headers = default(Headers)

@Tratcher
Copy link
Member

Hey @benaadams, while you're at it... #628

{
RequestHeaders = null;
ResponseHeaders = null;
var frameHeaders = _frameHeaders;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure this pattern is really achieving anything.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is to detect double call; also to drop references that have been returned to pool

@@ -35,7 +36,7 @@ public ValueTask<int> ReadAsync(ArraySegment<byte> buffer, CancellationToken can
return result;
}

public async Task Consume(CancellationToken cancellationToken = default(CancellationToken))
public Task Consume(CancellationToken cancellationToken = default(CancellationToken))
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shouldn't be in this set

@benaadams
Copy link
Contributor Author

Need to look at clearing methods

Is there any evidence this is faster than simply resetting all the fields without checking which bits are set like we did before?

@benaadams
Copy link
Contributor Author

Update description with measurements

@benaadams
Copy link
Contributor Author

Added fast header clear.

Note: Also you will need to switch on some pooling for high perf as headers now are created and dropped per request rather than per connection.

@benaadams
Copy link
Contributor Author

So with the inner struct clearing out all the header strings in ClearFast isn't much il

  IL_0009:  ldflda     valuetype HeaderReferences FrameRequestHeaders::_headers
  IL_000e:  initobj HeaderReferences

Full function

.method family hidebysig virtual instance void 
        ClearFast() cil managed
{
  // Code size       37 (0x25)
  .maxstack  8
  IL_0000:  ldarg.0
  IL_0001:  ldc.i4.0
  IL_0002:  conv.i8
  IL_0003:  stfld      int64 Microsoft.AspNetCore.Server.Kestrel.Http.FrameRequestHeaders::_bits
  IL_0008:  ldarg.0
  IL_0009:  ldflda     valuetype Microsoft.AspNetCore.Server.Kestrel.Http.FrameRequestHeaders/HeaderReferences Microsoft.AspNetCore.Server.Kestrel.Http.FrameRequestHeaders::_headers
  IL_000e:  initobj    Microsoft.AspNetCore.Server.Kestrel.Http.FrameRequestHeaders/HeaderReferences
  IL_0014:  ldarg.0
  IL_0015:  ldfld      class [System.Collections]System.Collections.Generic.Dictionary`2<string,valuetype [Microsoft.Extensions.Primitives]Microsoft.Extensions.Primitives.StringValues> Microsoft.AspNetCore.Server.Kestrel.Http.FrameHeaders::MaybeUnknown
  IL_001a:  dup
  IL_001b:  brtrue.s   IL_001f
  IL_001d:  pop
  IL_001e:  ret
  IL_001f:  call       instance void class [System.Collections]System.Collections.Generic.Dictionary`2<string,valuetype [Microsoft.Extensions.Primitives]Microsoft.Extensions.Primitives.StringValues>::Clear()
  IL_0024:  ret
} // end of method FrameRequestHeaders::ClearFast

@halter73
Copy link
Member

🚢

_frameHeaders = HttpComponentFactory.CreateHeaders(DateHeaderValueManager);
RequestHeaders = _frameHeaders.RequestHeaders;
ResponseHeaders = _frameHeaders.ResponseHeaders;
return this;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't return this. I don't see you using this anywhere anyway.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On second thought, don't worry about this. I changed it while addressing my own PR feedback readying a merge.

@benaadams benaadams deleted the pool-headers branch May 10, 2016 10:59
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants