-
Notifications
You must be signed in to change notification settings - Fork 5k
Use IndexOf in StringBuilder.Replace(string, string, ...) #81098
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
Conversation
StringBuilder.Replace(string, string, ...) currently walks character-by-character to find the next location of the oldValue. This change adds the use of MemoryExtensions.IndexOf to search for the oldValue, and only falls back to the character-by-character walk when it nears and needs to account for a chunk boundary. It also switches to track replacement indices using more stack space and the array pool (via ValueListBuilder) rather than less stack space and array allocations.
Tagging subscribers to this area: @dotnet/area-system-runtime Issue DetailsStringBuilder.Replace(string, string, ...) currently walks character-by-character to find the next location of the oldValue. This change adds the use of MemoryExtensions.IndexOf to search for the oldValue, and only falls back to the character-by-character walk when it nears and needs to account for a chunk boundary. It also switches to track replacement indices using more stack space and the array pool (via ValueListBuilder) rather than less stack space and array allocations. Using the benchmark from #80952:
Fixes #80952
|
src/libraries/System.Private.CoreLib/src/System/Text/StringBuilder.cs
Outdated
Show resolved
Hide resolved
@adamsitnik, the assert issue is fixed. Can you take another look? Thanks. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, thank you very much @stephentoub !
@@ -1934,8 +1934,7 @@ public StringBuilder Replace(string oldValue, string? newValue, int startIndex, | |||
|
|||
newValue ??= string.Empty; | |||
|
|||
Span<int> replacements = stackalloc int[5]; // A list of replacement positions in a chunk to apply | |||
int replacementsCount = 0; | |||
var replacements = new ValueListBuilder<int>(stackalloc int[128]); // A list of replacement positions in a chunk to apply |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
great use case for ValueListBuilder
👍 I am surprised that the temporary array allocation survived for so long in this repo ;)
runtime/src/libraries/System.Private.CoreLib/src/System/Text/StringBuilder.cs
Lines 1953 to 1958 in 1b22d37
if (replacementsCount >= replacements.Length) | |
{ | |
int[] tmp = new int[replacements.Length * 3 / 2 + 4]; // Grow by ~1.5x, but more in the beginning | |
replacements.CopyTo(tmp); | |
replacements = tmp; | |
} |
{ | ||
indexInChunk++; | ||
--count; | ||
if (StartsWith(chunk, indexInChunk, count, oldValue)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this method is the only method using StartsWith
, would we gain anything from inlining it? Would you mind running the benchmarks if you still have this branch built on your machine? If not, it's not worth your time (I am just curious and it's really hard to suggest any improvements for this PR)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if you still have this branch built on your machine
I don't. We can follow-up on your question separately, though.
StringBuilder.Replace(string, string, ...) currently walks character-by-character to find the next location of the oldValue. This change adds the use of MemoryExtensions.IndexOf to search for the oldValue, and only falls back to the character-by-character walk when it nears and needs to account for a chunk boundary. It also switches to track replacement indices using more stack space and the array pool (via ValueListBuilder) rather than less stack space and array allocations.
Using the benchmark from #80952:
Fixes #80952