Skip to content

Fast-path in String.Trim - #84300

Merged
EgorBo merged 2 commits into
dotnet:mainfrom
EgorBo:string-trim-fastpath
Apr 4, 2023
Merged

Fast-path in String.Trim#84300
EgorBo merged 2 commits into
dotnet:mainfrom
EgorBo:string-trim-fastpath

Conversation

@EgorBo

@EgorBoEgorBo commented Apr 4, 2023

Copy link
Copy Markdown
Member

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.:

stringTrimLiteral()=>"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)]publicstringTrim(stringstr)=>str.Trim();[Benchmark]publicstringTrimLiteral()=>"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 |

@ghostghost added the needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners label Apr 4, 2023
@ghostghost assigned EgorBoApr 4, 2023
@EgorBo
EgorBo requested a review from stephentoubApril 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
Copy Markdown
MemberAuthor

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
Copy Markdown
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.

@stephentoubstephentoub 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

ghost commented Apr 4, 2023

Copy link
Copy Markdown

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.:

stringTrimLiteral()=>"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)]publicstringTrim(stringstr)=>str.Trim();[Benchmark]publicstringTrimLiteral()=>"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 freeto 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

@EgorBo@stephentoub