You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
http2: client throughput capped at 64 KiB per round trip (connection window cannot be raised) #1997
A client built on package:http2 is much slower than dart:io's HttpClient (HTTP/1.1) over the same path once there is latency. Measured
with a 4 MiB response from a Go net/http server, through a local proxy that
adds 100 ms of round-trip time:
Client
Time
dart:ioHttpClient (HTTP/1.1)
~350 ms
package:http2, default settings
7.1 s
package:http2, streamWindowSize: 16 MiB
7.1 s
Three causes, in order of weight:
The connection receive window is fixed at 65535 bytes. HTTP/2 has no
setting for it; it grows only by a WINDOW_UPDATE on stream 0, and the
package never sends one beyond replenishing what was consumed. So at most
64 KiB is in flight per connection per round trip, whatever streamWindowSize is.
A raised streamWindowSize only counts from the peer's SETTINGS ACK.
Receive windows of new streams use the acknowledged settings, so a
response that starts within the first round trip is capped at 64 KiB for
that round trip, and a frame larger than 65535 bytes arriving before the
ACK fails flow control.
One WINDOW_UPDATE per DATA frame, for both the stream and the
connection (the TODO in IncomingWindowHandler.dataProcessed), plus
16 KiB frames: about 800 small frames and writes per 4 MiB, roughly
60 ms of CPU per 4 MiB in the measurement above.
Proposal (a PR is ready and follows this issue):
Settings.connectionWindowSize, granted by one WINDOW_UPDATE on stream 0
after the initial SETTINGS;
Settings.maxFrameSize, sent as SETTINGS_MAX_FRAME_SIZE;
the initial streamWindowSize and maxFrameSize in force for this end's
receive side from the moment they are sent (no stream exists yet, and the
peer reads the SETTINGS before any stream);
above the default 65535-byte window, one WINDOW_UPDATE per half window
processed instead of per frame; default-sized windows unchanged;
defaults unchanged.
With streamWindowSize: 6 MiB, connectionWindowSize: 16 MiB, maxFrameSize: 1 MiB the same transfer takes ~340 ms (~140 ms without the
added delay, against ~155 ms for HTTP/1.1).
A related latent issue, for the maintainers' judgement: when this end's own SETTINGS_INITIAL_WINDOW_SIZE is acknowledged, SettingsHandler emits onInitialWindowSizeChange, which StreamHandler applies to the send
windows of open streams (RFC 9113 section 6.9.2 reserves that for the
peer's setting). It is unreachable today because settings are sent only
once, at connection setup, and with the proposal above the acknowledged
value is already in place (difference zero); it would matter if settings
could be changed later.
Activity