-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Use CompareOrdinalHelper for SpanHelpers.SequenceCompareTo #22479
Conversation
Debug.Assert(*(a + 1) != *(b + 1), "This char must be different if we reach here!"); | ||
return *(a + 1) - *(b + 1); | ||
} | ||
return SpanHelpers.SequenceCompareTo(ref strA.GetRawStringData(), strA.Length, ref strB.GetRawStringData(), strB.Length); |
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.
Does this have any impact positive or negative on perf, in particular for short strings?
Also, I believe call sites to this were already checking the first character. Is that still needed? Is the assert above still valid?
I love the reduction in code, just want to make sure we understand any unanticipated consequences.
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.
Cleaned up the call sites as they were making more calls to the throw check than they needed to; but they are big switch methods, so is doubtful they will inline (which the comment it about), the _firstChar
check is without bounds check so quite light, so probably remains an easy optimization to skip the method call; if its a common case.
Will get some numbers.
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.
Actually one of the callsites might inline; but then the first char check would also inline, but SequenceCompareTo definitely won't; so it still works.
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.
On #22187, changing char in last position of string
Method | Length | Mean | Error | StdDev |
---------------------- |------- |----------:|----------:|----------:|
- string_CompareOrdinal | 1 | 3.204 ns | 3.5938 ns | 0.1970 ns |
+ string_CompareOrdinal | 1 | 3.159 ns | 0.2917 ns | 0.0160 ns |
- string_CompareOrdinal | 4 | 6.343 ns | 0.2986 ns | 0.0164 ns |
+ string_CompareOrdinal | 4 | 9.657 ns | 0.6693 ns | 0.0367 ns |
- string_CompareOrdinal | 5 | 6.561 ns | 0.4839 ns | 0.0265 ns |
+ string_CompareOrdinal | 5 | 6.955 ns | 1.4324 ns | 0.0785 ns |
- string_CompareOrdinal | 6 | 6.611 ns | 0.4435 ns | 0.0243 ns |
+ string_CompareOrdinal | 6 | 7.945 ns | 0.4340 ns | 0.0238 ns |
- string_CompareOrdinal | 7 | 7.400 ns | 0.4771 ns | 0.0262 ns |
+ string_CompareOrdinal | 7 | 8.733 ns | 0.6226 ns | 0.0341 ns |
- string_CompareOrdinal | 8 | 7.438 ns | 0.4543 ns | 0.0249 ns |
+ string_CompareOrdinal | 8 | 7.501 ns | 0.1890 ns | 0.0104 ns |
- string_CompareOrdinal | 15 | 6.256 ns | 0.2924 ns | 0.0160 ns |
+ string_CompareOrdinal | 15 | 8.128 ns | 0.1217 ns | 0.0067 ns |
- string_CompareOrdinal | 16 | 6.232 ns | 0.2279 ns | 0.0125 ns |
+ string_CompareOrdinal | 16 | 6.999 ns | 0.2556 ns | 0.0140 ns |
- string_CompareOrdinal | 24 | 9.363 ns | 0.3676 ns | 0.0201 ns |
+ string_CompareOrdinal | 24 | 7.577 ns | 0.4276 ns | 0.0234 ns |
- string_CompareOrdinal | 31 | 9.098 ns | 0.2410 ns | 0.0132 ns |
+ string_CompareOrdinal | 31 | 7.594 ns | 0.4550 ns | 0.0249 ns |
- string_CompareOrdinal | 32 | 9.105 ns | 0.5410 ns | 0.0297 ns |
+ string_CompareOrdinal | 32 | 7.578 ns | 0.2211 ns | 0.0121 ns |
- string_CompareOrdinal | 33 | 10.227 ns | 0.1747 ns | 0.0096 ns |
+ string_CompareOrdinal | 33 | 8.418 ns | 0.3742 ns | 0.0205 ns |
- string_CompareOrdinal | 63 | 9.809 ns | 0.4018 ns | 0.0220 ns |
+ string_CompareOrdinal | 63 | 9.311 ns | 0.7240 ns | 0.0397 ns |
- string_CompareOrdinal | 64 | 9.833 ns | 0.0420 ns | 0.0023 ns |
+ string_CompareOrdinal | 64 | 9.340 ns | 2.9041 ns | 0.1592 ns |
- string_CompareOrdinal | 127 | 15.538 ns | 0.7282 ns | 0.0399 ns |
+ string_CompareOrdinal | 127 | 12.648 ns | 1.3036 ns | 0.0715 ns |
- string_CompareOrdinal | 128 | 15.628 ns | 1.0243 ns | 0.0561 ns |
+ string_CompareOrdinal | 128 | 12.595 ns | 0.3935 ns | 0.0216 ns |
- string_CompareOrdinal | 255 | 23.341 ns | 0.9011 ns | 0.0494 ns |
+ string_CompareOrdinal | 255 | 19.403 ns | 0.2229 ns | 0.0122 ns |
- string_CompareOrdinal | 256 | 23.223 ns | 0.3965 ns | 0.0217 ns |
+ string_CompareOrdinal | 256 | 19.455 ns | 0.5873 ns | 0.0322 ns |
- string_CompareOrdinal | 1023 | 85.174 ns | 4.8935 ns | 0.2682 ns |
+ string_CompareOrdinal | 1023 | 73.952 ns | 0.7607 ns | 0.0417 ns |
- string_CompareOrdinal | 1024 | 85.041 ns | 7.0647 ns | 0.3872 ns |
+ string_CompareOrdinal | 1024 | 73.840 ns | 5.9904 ns | 0.3284 ns |
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.
The new numbers generally look worse for <= 16. Is that expected?
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.
Yes currently as that's when vectors kick in; though I think I can turn it around.
Will wait till decision made on #22187 though as its complicated to try a coordinate across the two PRs and that's an improvement on current and I don't want to keep adding changes on changes as its already quite big.
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.
i.e. SequenceCompareTo
does char by char; this does long/int unrolls knowing alignment of string; however should be able to xor then find first changed char
@dotnet-bot test CentOS7.1 x64 Debug Innerloop Build please |
Gave it a little push, since its just a wrapper method now
|
@dotnet-bot test OSX10.12 x64 Checked Innerloop Build and Test |
@benaadams, are you still working on this? The PR you mentioned in "Will wait till decision made on #22187" has been closed. |
Will follow up after #22505 |
Thank you for your contribution. As announced in dotnet/coreclr#27549 this repository will be moving to dotnet/runtime on November 13. If you would like to continue working on this PR after this date, the easiest way to move the change to dotnet/runtime is:
|
70f6706
to
b6c0488
Compare
b6c0488
to
2c5a7b3
Compare
@@ -56,6 +56,8 @@ private static bool EqualsOrdinalIgnoreCase(string strA, string strB) | |||
|
|||
return CompareInfo.EqualsOrdinalIgnoreCase(ref strA.GetRawStringData(), ref strB.GetRawStringData(), strB.Length); | |||
} | |||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
private static unsafe int CompareOrdinalHelper(string strA, string strB) |
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 helper does not seem to be providing much value. Would it be better to just inline the call in the two places that this is caller, without all the Debug.Asserts that describe the custom contract?
Could you please share the up to date perf numbers? |
I'm having trouble building atm
|
Thank you for your contribution. As announced in #27549 the dotnet/runtime repository will be used going forward for changes to this code base. Closing this PR as no more changes will be accepted into master for this repository. If you’d like to continue working on this change please move it to dotnet/runtime. |
Rather than a custom implementation, use the Vectorized
SpanHelpers.SequenceCompareTo
forAs per (which already uses it)
Performance should be additionally improved after #22187
/cc @jkotas @stephentoub