Content-Length: 368369 | pFad | https://github.com/dotnet/runtime/pull/81098

7B Use IndexOf in StringBuilder.Replace(string, string, ...) by stephentoub · Pull Request #81098 · dotnet/runtime · GitHub
Skip to content

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

Merged
merged 3 commits into from
Jan 27, 2023

Conversation

stephentoub
Copy link
Member

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:

Method Toolchain Mean Error StdDev Ratio Allocated Alloc Ratio
Replace_StringReplace \main\corerun.exe 43.71 us 0.537 us 0.476 us 1.00 227.96 KB 1.00
Replace_StringBuilder \main\corerun.exe 507.24 us 3.251 us 2.882 us 11.60 62.11 KB 0.27
Replace_StringReplace \pr\corerun.exe 39.38 us 0.440 us 0.411 us 0.90 227.96 KB 1.00
Replace_StringBuilder \pr\corerun.exe 24.16 us 0.213 us 0.189 us 0.55 58.9 KB 0.26

Fixes #80952

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.
@ghost
Copy link

ghost commented Jan 24, 2023

Tagging subscribers to this area: @dotnet/area-system-runtime
See info in area-owners.md if you want to be subscribed.

Issue Details

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:

Method Toolchain Mean Error StdDev Ratio Allocated Alloc Ratio
Replace_StringReplace \main\corerun.exe 43.71 us 0.537 us 0.476 us 1.00 227.96 KB 1.00
Replace_StringBuilder \main\corerun.exe 507.24 us 3.251 us 2.882 us 11.60 62.11 KB 0.27
Replace_StringReplace \pr\corerun.exe 39.38 us 0.440 us 0.411 us 0.90 227.96 KB 1.00
Replace_StringBuilder \pr\corerun.exe 24.16 us 0.213 us 0.189 us 0.55 58.9 KB 0.26

Fixes #80952

Author: stephentoub
Assignees: -
Labels:

area-System.Runtime, tenet-performance

Milestone: 8.0.0

@stephentoub
Copy link
Member Author

@adamsitnik, the assert issue is fixed. Can you take another look? Thanks.

Copy link
Member

@adamsitnik adamsitnik left a 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
Copy link
Member

@adamsitnik adamsitnik Jan 27, 2023

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 ;)

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))
Copy link
Member

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)

Copy link
Member Author

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.

@stephentoub stephentoub merged commit f388963 into dotnet:main Jan 27, 2023
@stephentoub stephentoub deleted the improvesbreplace branch January 27, 2023 18:15
@ghost ghost locked as resolved and limited conversation to collaborators Feb 26, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Lacking performance of StringBuilder.Replace
2 participants








ApplySandwichStrip

pFad - (p)hone/(F)rame/(a)nonymizer/(d)eclutterfier!      Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

Fetched URL: https://github.com/dotnet/runtime/pull/81098

Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy