Skip to content

HTTP/3 file uploads is significantly slower comparing to HTTP/2 or an in house HTTP/3 client #14198

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
alexsn opened this issue Jul 16, 2024 · 4 comments
Assignees
Labels
HTTP/3 h3 or quic related performance

Comments

@alexsn
Copy link

alexsn commented Jul 16, 2024

I did this

I'm settings to a http3 transfer by using CURLOPT_HTTP_VERSION and capturing request stats, as can be seen below the H3 request send takes a lot longer to complete.

I expected the following

I expect the file upload using H3 to be at least as fast as H2, currently it's about 3 - 4 times slower:

Request stats

// Request stats using H2, notice the delta between last_byte_flushed_ms and first_byte_flushed_ms which is 314ms
{
  "dns_resolution_time_ms": 4,
  "request_queue_time_ms": 0,
  "request_header_size": 700,
  "time_to_last_byte_ms": 2718,
  "time_to_first_byte_ms": 2718,
  "total_request_time_ms": 2718,
  "status_code": 200,
  "request_body_size": 1022505,
  "last_byte_flushed_ms": 497,
  "first_byte_flushed_ms": 183,
  "connect_time_ms": 58,
  "response_body_size": 159,
  "http_method": "POST",
  "new_connection": true,
  "server_address": "2a03:2880:f042:112:face:b00c:0:2",
  "handshake_time_ms": 120,
  "response_header_size": 145,
  "http_version": "2.0"
}

// Same request using H3, notice the time it takes to send the request is 1028ms
{
  "dns_resolution_time_ms": 4,
  "request_queue_time_ms": 0,
  "request_header_size": 697,
  "time_to_last_byte_ms": 3102,
  "time_to_first_byte_ms": 3027,
  "total_request_time_ms": 3102,
  "status_code": 200,
  "request_body_size": 1022505,
  "last_byte_flushed_ms": 1112,
  "first_byte_flushed_ms": 84,
  "connect_time_ms": 17,
  "response_body_size": 159,
  "host_name": "rupload.facebook.com",
  "http_method": "POST",
  "new_connection": true,
  "server_address": "2a03:2880:f042:112:face:b00c:0:2",
  "handshake_time_ms": 60,
  "response_header_size": 162,
  "http_version": "3.0"
}

Analysis:

I've taken packet capture from libcurl transfer and it looks like libcurl sends 64k of data then blocks until it all frames are acked which suspends the upload for about 40ms-50ms on every 64k chunk
Screenshot 2024-07-16 at 16 32 53

When comparing this to a in house HTTP/3 client there are no hangs and the upload continues smoothly throughout the transfer
Screenshot 2024-07-16 at 16 36 25

Checking curl_ngtcp2.c it looks like there's a buffering issue where the obvious suspect is sendbuf_len_in_flight which is where the 64k part comes from and there are multiple locations waiting for this value to drop to zero (like here and here) before resuming.

I've tried setting CURLOPT_UPLOAD_BUFFERSIZE to a larger value (512kb) which does help (makes it about 2x faster) though it's still about 2x slower then H2.

curl/libcurl version

libcurl 8.8.0 with wolfssl / ngtcp2 / nghttp3 and nghttp2.

operating system

Linux 5.12.0-0 #1 SMP Mon Jun 26 17:52:47 PDT 2023 x86_64 x86_64 x86_64 GNU/Linux

@vszakats vszakats added performance HTTP/3 h3 or quic related labels Jul 16, 2024
@dfandrich
Copy link
Contributor

dfandrich commented Jul 16, 2024 via email

@icing
Copy link
Contributor

icing commented Jul 16, 2024

@alexsn nice find. You are probably right with your analysis that the flow window is only restarted once everything is acked. I'll have a look at how we can make this more "flowing".

@icing icing self-assigned this Jul 17, 2024
@icing
Copy link
Contributor

icing commented Jul 17, 2024

Added scorecard upload tests in #14208. These show the abysmal performance on HTTP/3 PUTs.

@alexsn
Copy link
Author

alexsn commented Jul 17, 2024

Added scorecard upload tests in #14208. These show the abysmal performance on HTTP/3 PUTs.

Can you test with #14209 applied?

alexsn pushed a commit to alexsn/curl that referenced this issue Jul 18, 2024
Currently we're waiting for sendbuf_len_in_flight to hit zero before
resuming upload which means we're blocking and waiting for _all_ acks to
arrive before sending more data. This causes significant delays especially
when ack delay is used on the server side.

The fix addresses several issues in h3 over ngtcp2:
  - On ack we now call nghttp3_conn_resume_stream() when we have more
    data to send.
  - upload_left was incorrectly computed on CF_CTRL_DATA_DONE_SEND as
    we need to subtract the ammount of data we have in flight.
  - Remove upload_blocked_len as we Curl_bufq_write call will do the
    right thing when called from cf_ngtcp2_send.

Closes curl#14198
@bagder bagder closed this as completed in f504db8 Jul 18, 2024
meslubi2021 pushed a commit to Unity-Curl/curl that referenced this issue Jul 19, 2024
Currently we're waiting for sendbuf_len_in_flight to hit zero before
resuming upload which means we're blocking and waiting for _all_ acks to
arrive before sending more data. This causes significant delays especially
when ack delay is used on the server side.

The fix addresses several issues in h3 over ngtcp2:
  - On ack we now call nghttp3_conn_resume_stream() when we have more
    data to send.
  - upload_left was incorrectly computed on CF_CTRL_DATA_DONE_SEND as
    we need to subtract the ammount of data we have in flight.
  - Remove upload_blocked_len as we Curl_bufq_write call will do the
    right thing when called from cf_ngtcp2_send.

Fixes curl#14198
Closes curl#14209
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
HTTP/3 h3 or quic related performance
Development

Successfully merging a pull request may close this issue.

4 participants