Repository navigation
Closed
Labels
enhancementNew feature or requestNew feature or requestseverity:lowPolish / minor winPolish / minor win
Description
Each buffered request in ioxide.http3, the managed HTTP/3, allocates about 1.85 KB:
- about 1.66 KB in its request stream and request, which are new for every stream; 1 KiB of that is the request's arena;
- about 185 B in the QUIC send queue's per-stream objects (ngtcp2: every QUIC stream that sends allocates an OutStream with its own Queue, about 185 B per HTTP/3 response #487).
Measured on main eb5f515 (paths under src/protocols/ioxide.http3/ unless named):
- App:
Playground/Http3/ManagedBuffered, path/. - Setup: 1 reactor pinned to one core, and
h2load --alpn-list=h3 -c 32 -m 16 -t 1pinned to another physical core: 616K 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 arena byte[1024] |
1,053 | Http3Request.Append (Http3Request.cs:76): Array.Resize from empty on the first field, through Qpack.TryDecodeFieldSection |
Http3Request |
190 | ReqStream.Request (Http3Connection.cs:77) |
Header array (ReadOnlyMemory<byte>, ReadOnlyMemory<byte>)[] |
153 | Http3Request.Freeze (Http3Request.cs:94) |
Field-range array (int, int, int, int)[] |
89 | Http3Request.AddField (Http3Request.cs:55) |
ReqStream |
74 | Http3Connection.cs:297 |
ReqStream.Carry, a byte[16] |
41 | Http3Connection.cs:81 |
The request's two Lists |
32 + 30 | Http3Request.cs:20 and :34 |
OutStream, its Queue and the queue's array |
55 + 39 + 89 | QuicEngineConnection.SendStream, see #487 |
The streamed sample (Playground/Http3/ManagedStreamedBoth, 8 chunks of 1 KiB) allocates 2.78 KB per request:
- the same request objects;
- an
Http3BodyReader(139 B) and its chunkQueue(38 B) per request; - larger send-queue arrays (ngtcp2: every QUIC stream that sends allocates an OutStream with its own Queue, about 185 B per HTTP/3 response #487).
Its flushes allocate nothing measurable. They wait only when the QUIC send queue is full, and this load never fills it.
Possible fix: the one #486 applies to ioxide.http2.
- Keep a
ReqStreamon its connection once its handler is done, and reuse it for the next stream with itsHttp3Request, arena, lists and carry.- Today a stream leaves
_requestsat dispatch (Http3Connection.cs:642,:667) or on reset (:258,:503).
- Today a stream leaves
- 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.
In ioxide.http2, the same change took a request from 793 B to 89 B, and server CPU per request down by 17%. An HTTP/3 request costs more CPU (QUIC crypto and packets), so the share here needs measuring.
Metadata
Metadata
Assignees
Labels
enhancementNew feature or requestNew feature or requestseverity:lowPolish / minor winPolish / minor win
Activity