Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

[System.Globalization.Native] Fix small issues in CloneCollatorWithOptions and GetCollatorFromSortHandle #24100

Merged
merged 5 commits into from
Apr 25, 2019

Conversation

filipnavara
Copy link
Member

@filipnavara filipnavara commented Apr 18, 2019

The collator object returned by CloneCollatorWithOptions is cached so this is not a performance critical path. However, some mistakes were made in PR #22378 that made it perform a little less efficiently than it used to.

The performance-critical path in GetCollatorFromSortHandle was using binary search trees for something that could have been handled much easier with a small 32-entry lookup table. This is alternative fix to #24099 for an observed performance regression.

/cc @janvorli @stephentoub @jkotas

@filipnavara filipnavara changed the title [System.Globalization.Native] Fix small issues in CloneCollatorWithOptions [System.Globalization.Native] Fix small issues in CloneCollatorWithOptions and GetCollatorFromSortHandle Apr 19, 2019
@jkotas
Copy link
Member

jkotas commented Apr 19, 2019

Could you please resolve the conflicts?

Allocate initial array using malloc to avoid needlessly zeroing it
Reinstate optimization for empty array returned from GetCustomRules lost in dotnet#22378
@filipnavara
Copy link
Member Author

Could you please resolve the conflicts?

Done.

@@ -184,19 +157,18 @@ static UCharList* GetCustomRules(int32_t options, UColAttributeValue strength, i
}

// If we need to create customRules, the KanaType custom rule will be 88 kana characters * 4 = 352 chars long
// and the Width custom rule will be at least 215 halfwidth characters * 4 = 860 chars long.
// and the Width custom rule will be at most 212 halfwidth characters * 5 = 1060 chars long.
Copy link
Member

Choose a reason for hiding this comment

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

212 [](start = 49, length = 3)

How did you get 212 here?

looking at https://unicode.org/charts/PDF/UFF00.pdf, If considering all half width characters then this number would be really 201.

Also, the Kana characters should be 96?

I am ok to use a bigger numbers as I am not expecting the allocation will be much bigger anyway.

Copy link
Member

Choose a reason for hiding this comment

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

linking the original PR introduced creating this custom rule #2196


In reply to: 277037987 [](ancestors = 277037987)

Copy link
Member Author

Choose a reason for hiding this comment

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

How did you get 212 here?

The constant g_HalfFullCharsLength declared in the file. The description could be wrong though.

Also, the Kana characters should be 96?

Taken from the actual loop and comments in the same function.

@@ -206,15 +178,11 @@ static UCharList* GetCustomRules(int32_t options, UColAttributeValue strength, i
// Hiragana is the range 3041 to 3096 & 309D & 309E
if (hiraganaChar <= 0x3096 || hiraganaChar >= 0x309D) // characters between 3096 and 309D are not mapped to katakana
Copy link
Member

Choose a reason for hiding this comment

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

0x309D [](start = 58, length = 6)

This should be 309F now?

Copy link
Member

Choose a reason for hiding this comment

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

https://unicode.org/charts/PDF/U3040.pdf


In reply to: 277039452 [](ancestors = 277039452)

Copy link
Member Author

Choose a reason for hiding this comment

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

Do you prefer me to update it here or in a separate PR?

Copy link
Member

Choose a reason for hiding this comment

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

Maybe @eerhardt can comment why we didn't include 309F in the first place as this introduced awhile ago in Unicode 3.2. but you shouldn't block on this.

@tarekgh
Copy link
Member

tarekgh commented Apr 19, 2019

CC @eerhardt as he is the one originally introduced this code.

Copy link
Member

@tarekgh tarekgh left a comment

Choose a reason for hiding this comment

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

Would be nice if double check the constant values we used here. as I mentioned, using bigger values should be ok for allocation.

@filipnavara
Copy link
Member Author

Would be nice if double check the constant values we used here.

I would be happy if someone takes a second look. I used the actual constants from the two loops in the CloneCollatorWithOptions function.

}
*(items++) = lowerChar;
Copy link
Member

Choose a reason for hiding this comment

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

This should be hardened for potential buffer overruns. It is dangerous to allocate a buffer using complex buffer size formula and then start filling it using complex algorithm assuming that the formula and algorithm are in sync.

I would keep the Add method method and assert+fail if the buffer is not big enough.

Copy link
Member Author

Choose a reason for hiding this comment

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

There is already an assert on both places where this is used (line 206 and 179).

Copy link
Member

Choose a reason for hiding this comment

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

I would make the method fail in release build too.

Copy link
Member Author

Choose a reason for hiding this comment

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

I don't think that's necessary. There's a very limited number of input values (31) and they should all be exercised through the CoreFX tests. The CI should be able to catch any error in the debug build and bail out on the assert.

I will change the calculation to get rid of the magic 88 value in favour of the constraints from the first loop.

I'll also go and check that CoreFX actually has tests for this.

Copy link
Member Author

@filipnavara filipnavara Apr 21, 2019

Choose a reason for hiding this comment

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

The tests in https://github.com/dotnet/corefx/blob/a10890f4ffe0fadf090c922578ba0e606ebdd16c/src/System.Globalization/tests/CompareInfo/CompareInfoTests.Compare.cs hit both of the loops in the GetCustomRules function. If the code was broken the asserts would fire on the CI.

Copy link
Member

@eerhardt eerhardt left a comment

Choose a reason for hiding this comment

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

I think this looks good.

I only have a slight concern that if we add new CompareOptions value in the future (like adding real StringSort support or a new enum value), we may need to change this implementation. But we haven't added one in a long time, so going with this approach for now seems fine. (Looks like we haven't added one since we added OrdinalIgnoreCase in the .NET Framework 2.0.)

@jkotas jkotas merged commit a59ead8 into dotnet:master Apr 25, 2019
@sebastienros
Copy link
Member

This change has probably impacted the HttpClient benchmarks ... in a good way. From 80K to 105K RPS on Linux for our proxying scenario where headers have to be duplicated.

/cc @benaadams

@eerhardt
Copy link
Member

eerhardt commented May 7, 2019

@sebastienros - I am surprised by that. I would assume that scenario should be using Ordinal or OrdinalIgnoreCase options for header comparisons and not culture sensitive comparisons. But my understanding of HTTP is definitely not near the "expert" category, so I may be missing something here.

Is there any way to get comparisons before and after just this one change? And if we are showing major improvement - can we get a perfcollect for each?

@sebastienros
Copy link
Member

We changed it recently in this PR https://github.com/aspnet/AspNetCore/pull/9537/files

I can get perfcollect traces, yes, and I did the comparison by only switching the builds of System.Globalization.Native from these versions:

3.0.0-preview6-27629-07 -> 3.0.0-preview6-27701-08
Microsoft.NetCore.App / Core FX
dotnet/corefx@7066a0c...787f87f

Microsoft.NetCore.App / Core CLR
8808f4e...7a24a53

@sebastienros
Copy link
Member

Traces:
\scratch2\scratch\sebros\System.Globalization

@eerhardt
Copy link
Member

eerhardt commented May 7, 2019

Thanks! Downloading and taking a look now.

picenka21 pushed a commit to picenka21/runtime that referenced this pull request Feb 18, 2022
…tions and GetCollatorFromSortHandle (dotnet/coreclr#24100)

* Fix allocation size calculation when resizing array
Allocate initial array using malloc to avoid needlessly zeroing it
Reinstate optimization for empty array returned from GetCustomRules lost in dotnet/coreclr#22378

* Avoid resizing arrays in GetCustomRules by preallocating the maximum size we can consume (5648 bytes)

* Avoid creating a binary search tree for something that could be easily stored as 32-entry lookup table

* Remove obsolete comment


Commit migrated from dotnet/coreclr@a59ead8
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants