Skip to content

Copy in blocks in StringBuilder.CopyTo(int, Span<char>, int) - #694

Merged
SimonCropp merged 1 commit into
mainfrom
perf/stringbuilder-copyto
Oct 12, 2026
Merged

SimonCropp merged 1 commit into
mainfrom
perf/stringbuilder-copyto

Conversation

@SimonCropp

Copy link
Copy Markdown
Owner

The StringBuilder.CopyTo(int, Span<char>, int) polyfill (active on .NET Framework / netstandard2.0, where the BCL has no span overload) copied one char at a time through the StringBuilder indexer. On those runtimes the indexer walks the chunk list from the end for every call, so the copy costs O(count x chunks).

It now copies in blocks of up to 16,384 chars: the BCL's own CopyTo(int, char[], int, int) fills a buffer rented from ArrayPool<char>.Shared, and the buffer is copied to the destination span. Argument validation and the exceptions thrown are untouched, and nothing is rented when count is 0.

Measurements

net48, Release, x64, best of 3, a 1,000,000 char builder made of 100,000 appends, copied in full:

before after
77.04 ms 0.17 ms

Tests

Added to PolyfillTests_StringBuilder.cs:

  • StringBuilderCopyTo_MultiChunk: a 50,000 char builder assembled from many small appends (so it has many chunks), with 14 sourceIndex/count combinations either side of the 16,384 block size (16,383 / 16,384 / 16,385, two blocks, two blocks plus one), ranges that start mid-builder and end at the last char, and zero-length copies at the middle and the very end. Each case checks the copied text against ToString().Substring(...), that nothing past count is written to a larger destination, and that the builder is unchanged.
  • StringBuilderCopyTo_ExactDestination, StringBuilderCopyTo_Empty
  • the existing StringBuilderCopyTo_InvalidArgs still covers the exceptions

@SimonCropp SimonCropp added this to the 11.4.5 milestone Oct 12, 2026
@SimonCropp
SimonCropp merged commit 5466e75 into main Oct 12, 2026
5 checks passed
@SimonCropp
SimonCropp deleted the perf/stringbuilder-copyto branch October 12, 2026 12:07
This was referenced Oct 12, 2026
This was referenced Oct 14, 2026
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.

1 participant