Skip to content

Avoid linked CTS for non-cancellable pipe reads - #1144

Merged
AArnott merged 2 commits into
dotnet:mainfrom
karpinsn:perf/20260730-vs-startup-alloc-cpu/06-pipe-reader-cts
Aug 4, 2026
Merged

AArnott merged 2 commits into
dotnet:mainfrom
karpinsn:perf/20260730-vs-startup-alloc-cpu/06-pipe-reader-cts

Conversation

@karpinsn

@karpinsn karpinsn commented Aug 3, 2026

Copy link
Copy Markdown

Summary

  • Avoid creating a linked CancellationTokenSource when StreamPipeReader.ReadAsync receives a non-cancellable caller token.
  • Pass the reader cancellation token directly on that common path while retaining linked cancellation when both tokens participate.
  • Add coverage for token reuse, CancelPendingRead, caller cancellation, and a representative benchmark.

Performance

BenchmarkDotNet, .NET 8, 15 iterations with baseline/candidate/baseline:

  • Mean: 248.2 ns -> 205.9 ns per read (17.1% faster)
  • Allocated: 64 B -> 0 B per read
  • Baseline drift: 0.83%

Validation

  • Release test-project build: 0 warnings, 0 errors
  • Focused StreamUseStrictPipeReaderTests.ReadAsync_*: 10 passed across net8.0 and .NET Framework

Nik Karpinsky and others added 2 commits August 3, 2026 14:17
Use the reader cancellation token directly when the caller token cannot cancel, retaining a linked source when both tokens participate.

BenchmarkDotNet measured 64 B to 0 B per read and 17.1% lower elapsed time.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 22aae13c-50b4-4b0c-aee0-5a21e2b562c5
@AArnott

AArnott commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator

I pushed an update to reuse a vs-threading extension method. I expected/hoped the perf win would be roughly the same, but with slightly less code and avoiding allocations in even more cases.
As it turned out, it's allegedly even better. I'm measuring a 25% perf improvement.

On my machine...

Before (main)

Method Mean Error StdDev Gen0 Allocated
ReadAsyncWithCancellationTokenNone 155.1 ns 1.85 ns 1.73 ns 0.0038 64 B

After (this PR with my extra commit)

Method Mean Error StdDev Allocated
ReadAsyncWithCancellationTokenNone 117.0 ns 0.35 ns 0.29 ns -

@AArnott
AArnott enabled auto-merge August 4, 2026 15:31
@AArnott
AArnott added this pull request to the merge queue Aug 4, 2026
Merged via the queue into dotnet:main with commit 01fddc5 Aug 4, 2026
8 checks passed
@AArnott

AArnott commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator

@karpinsn Thanks for your PR!

@karpinsn

karpinsn commented Aug 4, 2026

Copy link
Copy Markdown
Author

I pushed an update to reuse a vs-threading extension method. I expected/hoped the perf win would be roughly the same, but with slightly less code and avoiding allocations in even more cases. As it turned out, it's allegedly even better. I'm measuring a 25% perf improvement.

On my machine...

Before (main)

Method Mean Error StdDev Gen0 Allocated
ReadAsyncWithCancellationTokenNone 155.1 ns 1.85 ns 1.73 ns 0.0038 64 B

After (this PR with my extra commit)

Method Mean Error StdDev Allocated
ReadAsyncWithCancellationTokenNone 117.0 ns 0.35 ns 0.29 ns -

Nice! I wasn't super happy with the style of the code before, this makes it so much cleaner. Thanks!

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants