Skip to content
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

[HTTP/3] Support for HTTP/3 multiple connections #101531

Merged
merged 28 commits into from
Jun 25, 2024

Conversation

ManickaP
Copy link
Member

@ManickaP ManickaP commented Apr 25, 2024

Feel free to review and discuss the API changes. This is NO MERGE until we have the APIs approved and all the TODOs resolved

The implementation takes H/2 connection pooling and slightly adjusts it for H/3 specific behaviors (see a diff between HttpConnectionPool.Http2.cs and HttpConnectionPool.Http3.cs).

The PR depends on 2 API reviews:

TODO: Add numbers from benchmarks when it runs
TODO: Stress run depends on dotnet/aspnetcore#55282

Fixes #51775
Fixes #54968
Fixes #68380
Resolves #101535
Resolves #101534

Copy link

Note regarding the new-api-needs-documentation label:

This serves as a reminder for when your PR is modifying a ref *.cs file and adding/modifying public APIs, please make sure the API implementation in the src *.cs file is documented with triple slash comments, so the PR reviewers can sign off that change.

@ManickaP ManickaP added the NO-MERGE The PR is not ready for merge yet (see discussion for detailed reasons) label Apr 25, 2024
@ManickaP ManickaP requested a review from a team April 25, 2024 08:30
@ManickaP
Copy link
Member Author

ManickaP commented Apr 25, 2024

No noticeable regression on perf from main:

main

  1. 1x10000 (clients x threads)
crank --config https://raw.githubusercontent.com/aspnet/Benchmarks/main/build/ci.profile.yml --config https://raw.githubusercontent.com/aspnet/Benchmarks/main/scenarios/httpclient.benchmarks.yml --client.framework net9.0 --server.framework net9.0 --scenario httpclient-kestrel-get --profile intel-lin-app --profile amd-lin2-load --variable useHttpMessageInvoker=true --variable httpVersion=3.0 --variable useHttps=true --variable responseSize=256 --variable numberOfHttpClients=1 --variable concurrencyPerHttpClient=10000
| client                      |                                       |
| --------------------------- | ------------------------------------- |
| Max CPU Usage (%)           | 35                                    |
| Max Cores usage (%)         | 971                                   |
| Max Working Set (MB)        | 3,810                                 |
| Max Private Memory (MB)     | 4,415                                 |
| Build Time (ms)             | 6,988                                 |
| Start Time (ms)             | 0                                     |
| Published Size (KB)         | 74,115                                |
| Symbols Size (KB)           | 19                                    |
| .NET Core SDK Version       | 9.0.100-preview.5.24224.9             |
| ASP.NET Core Version        | 9.0.0-preview.4.24223.1+f18510c2fbdf  |
| .NET Runtime Version        | 9.0.0-preview.4.24223.11+d92ac1f892a7 |
| Processor Count             | 28                                    |
| First request duration (ms) | 519                                   |
| Requests                    | 345,446                               |
| Bad Status Code Requests    | 0                                     |
| Exceptions                  | 0                                     |
| Mean RPS                    | 23,069                                |
  1. 1x100 (clients x threads)
crank --config https://raw.githubusercontent.com/aspnet/Benchmarks/main/build/ci.profile.yml --config https://raw.githubusercontent.com/aspnet/Benchmarks/main/scenarios/httpclient.benchmarks.yml --client.framework net9.0 --server.framework net9.0 --scenario httpclient-kestrel-get --profile intel-lin-app --profile amd-lin2-load --variable useHttpMessageInvoker=true --variable httpVersion=3.0 --variable useHttps=true --variable responseSize=256 --variable numberOfHttpClients=1 --variable concurrencyPerHttpClient=100
| client                      |                                       |
| --------------------------- | ------------------------------------- |
| Max CPU Usage (%)           | 41                                    |
| Max Cores usage (%)         | 1,150                                 |
| Max Working Set (MB)        | 577                                   |
| Max Private Memory (MB)     | 1,119                                 |
| Build Time (ms)             | 4,926                                 |
| Start Time (ms)             | 0                                     |
| Published Size (KB)         | 74,115                                |
| Symbols Size (KB)           | 19                                    |
| .NET Core SDK Version       | 9.0.100-preview.5.24225.1             |
| ASP.NET Core Version        | 9.0.0-preview.4.24223.1+f18510c2fbdf  |
| .NET Runtime Version        | 9.0.0-preview.4.24223.11+d92ac1f892a7 |
| Processor Count             | 28                                    |
| First request duration (ms) | 520                                   |
| Requests                    | 989,468                               |
| Bad Status Code Requests    | 0                                     |
| Exceptions                  | 0                                     |
| Mean RPS                    | 65,955                                |
  1. 20x500 (clients x threads)
crank --config https://raw.githubusercontent.com/aspnet/Benchmarks/main/build/ci.profile.yml --config https://raw.githubusercontent.com/aspnet/Benchmarks/main/scenarios/httpclient.benchmarks.yml --client.framework net9.0 --server.framework net9.0 --scenario httpclient-kestrel-get --profile intel-lin-app --profile amd-lin2-load --variable useHttpMessageInvoker=true --variable httpVersion=3.0 --variable useHttps=true --variable responseSize=256 --variable numberOfHttpClients=20 --variable concurrencyPerHttpClient=500
| client                      |                                       |
| --------------------------- | ------------------------------------- |
| Max CPU Usage (%)           | 97                                    |
| Max Cores usage (%)         | 2,710                                 |
| Max Working Set (MB)        | 6,526                                 |
| Max Private Memory (MB)     | 7,268                                 |
| Build Time (ms)             | 2,544                                 |
| Start Time (ms)             | 0                                     |
| Published Size (KB)         | 74,115                                |
| Symbols Size (KB)           | 19                                    |
| .NET Core SDK Version       | 9.0.100-preview.5.24225.1             |
| ASP.NET Core Version        | 9.0.0-preview.4.24223.1+f18510c2fbdf  |
| .NET Runtime Version        | 9.0.0-preview.4.24223.11+d92ac1f892a7 |
| Processor Count             | 28                                    |
| First request duration (ms) | 518                                   |
| Requests                    | 4,976,883                             |
| Bad Status Code Requests    | 0                                     |
| Exceptions                  | 0                                     |
| Mean RPS                    | 331,779                               |

PR, multiple H3 connections OFF

  1. 1x10000 (clients x threads)
crank --config https://raw.githubusercontent.com/aspnet/Benchmarks/main/build/ci.profile.yml --config https://raw.githubusercontent.com/aspnet/Benchmarks/main/scenarios/httpclient.benchmarks.yml --client.framework net9.0 --server.framework net9.0 --scenario httpclient-kestrel-get --profile intel-lin-app --profile amd-lin2-load --variable useHttpMessageInvoker=true --variable httpVersion=3.0 --variable useHttps=true --variable responseSize=256 --variable numberOfHttpClients=1 --variable concurrencyPerHttpClient=10000 --client.options.outputFiles artifacts/bin/testhost/net9.0-linux-Release-x64/shared/Microsoft.NETCore.App/9.0.0/System.Net.Http.dll --client.options.outputFiles  artifacts/bin/testhost/net9.0-linux-Release-x64/shared/Microsoft.NETCore.App/9.0.0/System.Net.Quic.dll
| client                      |                                       |
| --------------------------- | ------------------------------------- |
| Max CPU Usage (%)           | 36                                    |
| Max Cores usage (%)         | 998                                   |
| Max Working Set (MB)        | 3,772                                 |
| Max Private Memory (MB)     | 4,371                                 |
| Build Time (ms)             | 2,866                                 |
| Start Time (ms)             | 0                                     |
| Published Size (KB)         | 74,115                                |
| Symbols Size (KB)           | 19                                    |
| .NET Core SDK Version       | 9.0.100-preview.5.24225.1             |
| ASP.NET Core Version        | 9.0.0-preview.4.24223.1+f18510c2fbdf  |
| .NET Runtime Version        | 9.0.0-preview.4.24223.11+d92ac1f892a7 |
| Processor Count             | 28                                    |
| First request duration (ms) | 596                                   |
| Requests                    | 342,841                               |
| Bad Status Code Requests    | 0                                     |
| Exceptions                  | 0                                     |
| Mean RPS                    | 22,914                                |
  1. 1x100 (clients x threads)
crank --config https://raw.githubusercontent.com/aspnet/Benchmarks/main/build/ci.profile.yml --config https://raw.githubusercontent.com/aspnet/Benchmarks/main/scenarios/httpclient.benchmarks.yml --client.framework net9.0 --server.framework net9.0 --scenario httpclient-kestrel-get --profile intel-lin-app --profile amd-lin2-load --variable useHttpMessageInvoker=true --variable httpVersion=3.0 --variable useHttps=true --variable responseSize=256 --variable numberOfHttpClients=1 --variable concurrencyPerHttpClient=100 --client.options.outputFiles artifacts/bin/testhost/net9.0-linux-Release-x64/shared/Microsoft.NETCore.App/9.0.0/System.Net.Http.dll --client.options.outputFiles  artifacts/bin/testhost/net9.0-linux-Release-x64/shared/Microsoft.NETCore.App/9.0.0/System.Net.Quic.dl
| client                      |                                       |
| --------------------------- | ------------------------------------- |
| Max CPU Usage (%)           | 46                                    |
| Max Cores usage (%)         | 1,296                                 |
| Max Working Set (MB)        | 587                                   |
| Max Private Memory (MB)     | 1,174                                 |
| Build Time (ms)             | 2,897                                 |
| Start Time (ms)             | 0                                     |
| Published Size (KB)         | 74,115                                |
| Symbols Size (KB)           | 19                                    |
| .NET Core SDK Version       | 9.0.100-preview.5.24225.1             |
| ASP.NET Core Version        | 9.0.0-preview.4.24223.1+f18510c2fbdf  |
| .NET Runtime Version        | 9.0.0-preview.4.24223.11+d92ac1f892a7 |
| Processor Count             | 28                                    |
| First request duration (ms) | 594                                   |
| Requests                    | 985,119                               |
| Bad Status Code Requests    | 0                                     |
| Exceptions                  | 0                                     |
| Mean RPS                    | 65,691                                |
  1. 20x500 (clients x threads)
rank --config https://raw.githubusercontent.com/aspnet/Benchmarks/main/build/ci.profile.yml --config https://raw.githubusercontent.com/aspnet/Benchmarks/main/scenarios/httpclient.benchmarks.yml --client.framework net9.0 --server.framework net9.0 --scenario httpclient-kestrel-get --profile intel-lin-app --profile amd-lin2-load --variable useHttpMessageInvoker=true --variable httpVersion=3.0 --variable useHttps=true --variable responseSize=256 --variable numberOfHttpClients=20 --variable concurrencyPerHttpClient=500 --client.options.outputFiles artifacts/bin/testhost/net9.0-linux-Release-x64/shared/Microsoft.NETCore.App/9.0.0/System.Net.Http.dll --client.options.outputFiles  artifacts/bin/testhost/net9.0-linux-Release-x64/shared/Microsoft.NETCore.App/9.0.0/System.Net.Quic.dll
| client                      |                                       |
| --------------------------- | ------------------------------------- |
| Max CPU Usage (%)           | 95                                    |
| Max Cores usage (%)         | 2,667                                 |
| Max Working Set (MB)        | 6,494                                 |
| Max Private Memory (MB)     | 7,246                                 |
| Build Time (ms)             | 2,660                                 |
| Start Time (ms)             | 0                                     |
| Published Size (KB)         | 74,115                                |
| Symbols Size (KB)           | 19                                    |
| .NET Core SDK Version       | 9.0.100-preview.5.24225.1             |
| ASP.NET Core Version        | 9.0.0-preview.4.24223.1+f18510c2fbdf  |
| .NET Runtime Version        | 9.0.0-preview.4.24223.11+d92ac1f892a7 |
| Processor Count             | 28                                    |
| First request duration (ms) | 594                                   |
| Requests                    | 5,334,724                             |
| Bad Status Code Requests    | 0                                     |
| Exceptions                  | 0                                     |
| Mean RPS                    | 355,707                               |

PR, multiple H3 connections ON

  1. 1x10000 (clients x threads)
crank --config https://raw.githubusercontent.com/aspnet/Benchmarks/main/build/ci.profile.yml --config https://raw.githubusercontent.com/aspnet/Benchmarks/main/scenarios/httpclient.benchmarks.yml --client.framework net9.0 --server.framework net9.0 --scenario httpclient-kestrel-get --profile intel-lin-app --profile amd-lin2-load --variable useHttpMessageInvoker=true --variable httpVersion=3.0 --variable useHttps=true --variable responseSize=256 --variable numberOfHttpClients=1 --variable concurrencyPerHttpClient=10000 --client.options.outputFiles artifacts/bin/testhost/net9.0-linux-Release-x64/shared/Microsoft.NETCore.App/9.0.0/System.Net.Http.dll --client.options.outputFiles  artifacts/bin/testhost/net9.0-linux-Release-x64/shared/Microsoft.NETCore.App/9.0.0/System.Net.Quic.dll
| client                      |                                      |
| --------------------------- | ------------------------------------ |
| Max CPU Usage (%)           | 92                                   |
| Max Cores usage (%)         | 2,572                                |
| Max Working Set (MB)        | 6,491                                |
| Max Private Memory (MB)     | 7,228                                |
| Build Time (ms)             | 2,736                                |
| Start Time (ms)             | 0                                    |
| Published Size (KB)         | 74,777                               |
| Symbols Size (KB)           | 19                                   |
| .NET Core SDK Version       | 9.0.100-preview.5.24272.19           |
| ASP.NET Core Version        | 9.0.0-preview.6.24272.7+49c1c68bf1ac |
| .NET Runtime Version        | 9.0.0-preview.5.24272.2+5c06e5d01fa0 |
| Processor Count             | 28                                   |
| First request duration (ms) | 594                                  |
| Requests                    | 4,325,325                            |
| Bad Status Code Requests    | 0                                    |
| Exceptions                  | 0                                    |
| Mean RPS                    | 288,664                              |

@ManickaP
Copy link
Member Author

/azp run runtime-libraries stress-http

Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@ManickaP
Copy link
Member Author

/azp run runtime-libraries-coreclr outerloop

Copy link

Azure Pipelines successfully started running 1 pipeline(s).

Comment on lines +228 to +234
private async void OnStreamCapacityIncreased(int bidirectionalIncrement, int unidirectionalIncrement)
{
// Bail out early to avoid queueing work on the thread pool as well as event args instantiation.
if (_streamCapacityCallback is null)
{
return;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will a call to this function allocate a state machine even when no callbacks are registered? Maybe we can delegate the latter half of the function to nested private helper function.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will "allocate" the state machine struct on the stack until the first await that yields.
In this case if _streamCapacityCallback is null, it won't allocate anything on the heap.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I get that it's not worth any change 😉

@ManickaP
Copy link
Member Author

/azp run runtime-libraries stress-http

Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@ManickaP
Copy link
Member Author

/azp run runtime-libraries-coreclr outerloop

Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@ManickaP
Copy link
Member Author

If the pipeline gods grant this PR green, I'll merge this. Last chance to scream any feedback 😄

@ManickaP
Copy link
Member Author

/azp run runtime-libraries-coreclr outerloop

Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@ManickaP
Copy link
Member Author

/azp run runtime-libraries stress-http

Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@ManickaP ManickaP merged commit c14e5dd into dotnet:main Jun 25, 2024
82 of 94 checks passed
@ManickaP ManickaP deleted the h3-mul-conn branch June 25, 2024 14:46
@github-actions github-actions bot locked and limited conversation to collaborators Jul 26, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.