Conversation
The existing SslStream benchmarks only ever handshake one connection at a time, so there is no coverage of the shape clients actually use against a single host: connection pools, and HTTP/2 and HTTP/3 pool warm-up, fanning out several handshakes at once. SslStreamConcurrencyTests measures per-handshake cost at concurrency levels 1, 8, 64 and 128, with and without session resumption, for TLS 1.2 and TLS 1.3. Unlike the other SslStream benchmarks this one runs over loopback sockets. Measured over in-memory streams, concurrent TLS 1.3 connections resume ~97% of the time at every concurrency level; over sockets the rate falls to ~40% at 128 concurrent connections. An in-memory version would measure the uncontended case whatever Concurrency was set to.
Contributor
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
The server-side accept/serve background tasks currently risk masking unexpected failures and producing unobserved task faults during cleanup, which can destabilize benchmark runs.
Get a fresh assessment by requesting another Copilot review.
Review effort: Lite
Findings: 1
Open (1)
What changed in this PR
Adds a new microbenchmark targeting concurrent TLS handshakes with SslStream, intended to better represent real client behavior (connection pools / HTTP/2+ warm-up) and to measure per-handshake cost across multiple concurrency levels and protocol/resumption combinations.
Changes:
- Introduces
SslStreamConcurrencyTests.ConcurrentHandshakemeasuring 128 handshakes per invocation in waves of configurable concurrency. - Adds protocol matrix (TLS 1.2 + optional TLS 1.3) and session-resumption toggle, with a
GlobalSetupassertion that resumption is actually occurring. - Implements a loopback-socket server accept/serve loop to drive concurrent client handshakes.
| File | Description |
|---|---|
| src/benchmarks/micro/libraries/System.Net.Security/SslStreamConcurrencyTests.cs | New benchmark class for concurrent SslStream handshakes over loopback sockets, parameterized by protocol, resumption, and concurrency. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Comment on lines
+135
to
+138
| catch | ||
| { | ||
| return; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

The existing
SslStreambenchmarks only ever handshake one connection at a time, so there is no coverage of the shape clients actually use against a single host: connection pools, and HTTP/2 and HTTP/3 pool warm-up, fanning out several handshakes at once.SslStreamConcurrencyTests.ConcurrentHandshakemeasures per-handshake cost at concurrency levels 1, 8, 64 and 128, with and without session resumption, for TLS 1.2 and TLS 1.3. That is 16 test cases, the maximumTooManyTestCasesValidatorallows for a single benchmark.It lives in its own class because
[Params]apply to every benchmark in a class, so adding these toSslStreamTestswould have multiplied its entire matrix.Why loopback sockets rather than in-memory streams
This is the one deliberate departure from the surrounding benchmarks, and it is required rather than incidental. Measuring the steady-state TLS 1.3 resumption rate under identical wave scheduling, varying only the transport:
ConnectedStreamsTLS 1.2 stays at 100% on both transports at every level. Over in-memory streams the concurrent case essentially does not occur, so an in-memory version of this benchmark would measure the uncontended path whatever
Concurrencywas set to.In-memory is also noticeably slower and noisier here — TLS 1.2 at 8 concurrent connections costs 1596 µs/handshake in memory versus 199 µs over sockets, because
StreamBufferoverhead dominates the TLS work.Results (Linux x64, OpenSSL 3.0.13, RSA-2048)
Resumed handshakes, µs each:
For concurrency ≥ 8 every cell reports under 2% error, and the numbers reproduced across repeated runs.
Notes
[GlobalSetup]asserts that resumption actually takes effect before measuring. Without that check theResume=truecases can silently measure full handshakes and still look like plausible results.LingerOption(true, 0)) once it has ingested its session ticket. A graceful close left a peak of ~85,000 sockets inTIME_WAIT, which is 65% of the defaulttcp_max_tw_bucketsand risks exhausting the ephemeral port range; this brings the peak down to ~122. Concurrent file descriptor use is bounded byConcurrency, not by run length.Note
This pull request description was generated with AI assistance.