Skip to content

HTTP/2: a 64 MiB response to x/net's http2.Transport takes ~16 s on epoll, io_uring and adaptive, 0.07 s on std (net/http's client: 0.3-0.6 s) #809

Description

@FumingPower3925

Found while working on #761. Pre-existing on main: measured on dfd044f.

Observation

A single 64 MiB HTTP/2 response (h2c, prior knowledge) to golang.org/x/net's http2.Transport takes about 16 s on epoll, io_uring and adaptive, where std serves it in 0.07 s to the same client. net/http's own client (http.Transport with Protocols.SetUnencryptedHTTP2) gets the same response from the native engines in 0.3-0.6 s, so what the slow case depends on is the client's flow-control behaviour, and the server side is what differs from std. 1 MiB and 5 MiB bodies take 0.02-0.05 s on every engine, so the cost grows with the number of window updates the transfer needs.

engine route 64 MiB to x/net http2.Transport (main dfd044f) same, #805 at 689cee4 64 MiB to net/http's client (#805 at 222b97e)
std sync / async 0.07 / 0.06 s 0.06 / 0.06 s
epoll sync / async 15.55 / 15.70 s 16.88 / 16.22 s 0.32 / 0.37 s
io_uring sync / async (closed at 4 MiB: #761) 21.38 / 15.95 s 0.63 / 0.53 s
adaptive sync / async 15.86 / 15.87 s 20.16 / 20.06 s 0.43 / 0.51 s

(x/net column: --- PASS durations of TestProbe761H2/<engine>/<route>/67108864, whose client has a 20 s timeout, so the 20-21 s cells ended at it; net/http column: TestLargeResponseIsDeliveredH2/<engine>/<route>/67108864. 689cee4 and 222b97e are earlier rounds of #805's branch; 222b97e is its head's code before the rebase onto bd17725. linux/arm64 Docker, unlimited memlock.)

Scripts: lane evidence lanes-20260927/WRITE/761/run-761.sh <ref> unlimited (probe 761/probe761_linux_test.go, test 761/large_response_linux_test.go). Logs: 761/logs/dfd044f-unlimited.log, 761/logs/689cee4-unlimited.log, 761/logs/222b97e-unlimited.log.

Not yet known

Which frames are slow was not traced. The rate, about 4 MB/s, and its independence of the route suggest the server acts on the client's WINDOW_UPDATEs late (a timer or a wait on the next recv) when the client grants small increments; a frame capture of both clients against epoll would show which. x/net 0.59's http2.Transport is deprecated in favour of net/http's, so the question is also whether other clients with small windows (browsers, curl/nghttp2) see it.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/engineEngine interface or implementationengine/epollEpoll engine specificsengine/iouringio_uring engine specificsperformancePerformance optimizationprotocol/h2HTTP/2 protocol

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions