Skip to content
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

Fast-path in String.Trim #84300

Merged
merged 2 commits into from
Apr 4, 2023
Merged

Fast-path in String.Trim #84300

merged 2 commits into from
Apr 4, 2023

Conversation

EgorBo
Copy link
Member

@EgorBo EgorBo commented Apr 4, 2023

Similar to #84210 but for String. I tried to use Span APIs here but there was a small overhead.

Bonus point - "string literal".Trim() is a no-op now, e.g.:

string TrimLiteral() => "Hello".Trim();
; Method Program:TrimLiteral():System.String:this
       48B9809520802B020000 mov      rcx, 0x22B80209580 ;; 'string literal'
-      BA03000000           mov      edx, 3
-      FF251BFE0400         tail.jmp [System.String:TrimWhiteSpaceHelper(int):System.String:this]
-; Total bytes of code: 21
+; Total bytes of code: 11

Since JIT is able to fold if (Length == 0 || (!char.IsWhiteSpace(_firstChar) && !char.IsWhiteSpace(this[^1]))) for a constant string 🙂 e.g. constant folding for RVA[cns]

Benchmarks:

[Benchmark]
[Arguments("Hello world")]
[Arguments("Hello world ")]
[MethodImpl(MethodImplOptions.NoInlining)]
public string Trim(string str) => str.Trim();

[Benchmark]
public string TrimLiteral() => "Hello".Trim();
|         Method |                   Toolchain |          str |      Mean |
|--------------- |---------------------------- |------------- |----------:|
|           Trim |      \Core_Root\corerun.exe |  Hello world | 0.4868 ns |
|           Trim | \Core_Root_base\corerun.exe |  Hello world | 2.0999 ns |
|                |                             |              |           |
|           Trim |      \Core_Root\corerun.exe | Hello world  | 7.4285 ns |
|           Trim | \Core_Root_base\corerun.exe | Hello world  | 6.4529 ns |
|                |                             |              |           |
|    TrimLiteral |      \Core_Root\corerun.exe |            ? | 0.0086 ns |
|    TrimLiteral | \Core_Root_base\corerun.exe |            ? | 2.0089 ns |

@dotnet-issue-labeler dotnet-issue-labeler bot added the needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners label Apr 4, 2023
@ghost ghost assigned EgorBo Apr 4, 2023
@EgorBo EgorBo requested a review from stephentoub April 4, 2023 14:43
public unsafe string Trim(char trimChar) => TrimHelper(&trimChar, 1, TrimType.Both);
public unsafe string Trim(char trimChar)
{
if (Length == 0 || (_firstChar != trimChar && this[^1] != trimChar))
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No bound checks here due to #84213

{
return this;
}
return TrimHelper(&trimChar, 1, TrimType.Both);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not for this PR, but now that we'll be invoking these helpers only when we know there is something to be trimmed, I wonder whether employing an IndexOfAnyExceptWhitespace would be more useful, i.e. whether there being any whitespace makes it more likely there's enough whitespace for vectorization to be a win.

@stephentoub stephentoub added area-System.Runtime and removed needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners labels Apr 4, 2023
@ghost
Copy link

ghost commented Apr 4, 2023

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

Issue Details

Similar to #84210 but for String. I tried to use Span APIs here but there was a small overhead.

Bonus point - "string literal".Trim() is a no-op now, e.g.:

string TrimLiteral() => "Hello".Trim();
; Method Program:TrimLiteral():System.String:this
       48B9809520802B020000 mov      rcx, 0x22B80209580 ;; 'string literal'
-      BA03000000           mov      edx, 3
-      FF251BFE0400         tail.jmp [System.String:TrimWhiteSpaceHelper(int):System.String:this]
-; Total bytes of code: 21
+; Total bytes of code: 11

Since JIT is able to fold if (Length == 0 || (!char.IsWhiteSpace(_firstChar) && !char.IsWhiteSpace(this[^1]))) for a constant string 🙂 e.g. constant folding for RVA[cns]

Benchmarks:

[Benchmark]
[Arguments("Hello world")]
[Arguments("Hello world ")]
[MethodImpl(MethodImplOptions.NoInlining)]
public string Trim(string str) => str.Trim();

[Benchmark]
public string TrimLiteral() => "Hello".Trim();
|         Method |                   Toolchain |          str |      Mean |
|--------------- |---------------------------- |------------- |----------:|
|           Trim |      \Core_Root\corerun.exe |  Hello world | 0.4868 ns |
|           Trim | \Core_Root_base\corerun.exe |  Hello world | 2.0999 ns |
|                |                             |              |           |
|           Trim |      \Core_Root\corerun.exe | Hello world  | 7.4285 ns |
|           Trim | \Core_Root_base\corerun.exe | Hello world  | 6.4529 ns |
|                |                             |              |           |
|    TrimLiteral |      \Core_Root\corerun.exe |            ? | 0.0086 ns |
|    TrimLiteral | \Core_Root_base\corerun.exe |            ? | 2.0089 ns |
Author: EgorBo
Assignees: EgorBo
Labels:

area-System.Runtime

Milestone: -

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants