Repository navigation
Closed
Labels
enhancementNew feature or requestNew feature or requestseverity:lowPolish / minor winPolish / minor win
Description
Each buffered request in ioxide.nghttp2 allocates about 655 B in objects made per request. It is the same set that #486 removes from ioxide.http2 for #485. Async machinery is small here: about 7 B per request, one FlushEgressAsync box per pass.
Measured on main eb5f515 (paths under src/protocols/ioxide.nghttp2/ unless named):
- App:
Playground/Http2/Nghttp2Buffered, path/. - Setup: 1 reactor pinned to one core, and
h2load -c 32 -m 16 -t 1pinned to another physical core: 1.16M req/s. - Method: a 10 s
dotnet-trace --profile gc-verbosewindow, allocations grouped by type and first ioxide frame.
| Allocated | B per request | Where |
|---|---|---|
Request header array KeyValuePair<ReadOnlyMemory<byte>, ReadOnlyMemory<byte>>[8] |
279 | First Headers.Add in PendingRequest.Freeze (Connection/Nghttp2Connection.cs:247) |
Nghttp2Request |
119 | PendingRequest.Freeze (Connection/Nghttp2Connection.cs:234) |
PendingRequest |
96 | CallbackBeginHeaders (Connection/Nghttp2Connection.Callbacks.cs:50) |
Field-range array (int, int, int, int)[] |
88 | AddField (Connection/Nghttp2Connection.cs:214), from CallbackHeader |
Request KeyValueList |
32 | Nghttp2Request.Headers (Http/Nghttp2Request.cs:29) |
Field-range List |
32 | PendingRequest._fields (Connection/Nghttp2Connection.cs:164) |
PendingRequest[] |
9 | _readyThisPass.ToArray() (Connection/Nghttp2Connection.RunBuffered.cs:138), one per pass |
FlushEgressAsync box |
7 | One per pass |
Nghttp2Response and its KeyValueList |
48 + 33 | The handler's own new Nghttp2Response (app code) |
The streamed sample (Playground/Http2/Nghttp2Response, 8 chunks of 1 KiB):
- It allocates the same request objects, about 660 B per request.
- Its flushes add about 19 B per request, or 2.4 B per flush. A flush that finds a drain already running returns at once instead of waiting, so the flush half of http2: a streamed flush that waits for the write pump allocates nothing, and a connection reuses its request objects #486 has nothing to fix here.
Possible fix: the one #486 applies to ioxide.http2.
- Return a retired
PendingRequestto its connection with itsNghttp2Request, headerKeyValueListand_fields, and reuse them for the next stream.- It is retired at the
pending.Dispose()calls inConnection/Nghttp2Connection.RunBuffered.cs, and inServeStreamedAsync(Connection/Nghttp2Connection.Streamed.cs:95). KeyValueList.Clearalready keeps its array for this.
- It is retired at the
- The request object is then reused once its handler is done, as in http2: a streamed flush that waits for the write pump allocates nothing, and a connection reuses its request objects #486. It was already only valid during the handler.
- Swap two lists instead of
_readyThisPass.ToArray().
In ioxide.http2, the same change took a request from 793 B to 89 B, and server CPU per request down by 17%.
Metadata
Metadata
Assignees
Labels
enhancementNew feature or requestNew feature or requestseverity:lowPolish / minor winPolish / minor win
Activity