Uh oh!
There was an error while loading. Please reload this page.
Simplify utf8.c - #102424
Conversation
9fc22ee to
020c8edComparecc @AaronRobinsonMSFT, re: #100451 (comment) - this mainly reduces the assembly code with cl.exe -O2 (before 1091 lines, now 369 for |
| // May have a problem if we have to flush | ||
| if (ch != 0) | ||
| if (sourceIndex < sourceLength && (destinationIndex > destinationLength || (destinationIndex == destinationLength && sourceIndex + 1 < sourceLength))) |
There was a problem hiding this comment.
@lambdageek, just an FYI, I had to change this condition from
if (sourceIndex < sourceLength && (destinationIndex >= destinationLength)
for mono because this case was failing:
IMHO, the reflection stack of mono should use the same expectation from giconv about the expected lengths as coreclr's reflection stack makes from coreclr PAL's unicode.cpp, so this standalone component doesn't need to handle more edge cases than necessary.
There was a problem hiding this comment.
The test that @am11 mentioned was testing the custom attribute value string "\uDFFF". For mono, it went through minipal_convert_utf8_to_utf16 to decode this string "\xed\xbf\xbf". It seems to me that CoreCLR went through a different mechanism to get the value of the custom attribute value.
Additionally, what Mono is doing in g_utf8_to_utf16_impl of giconv.c isn't a lot different than what is done in MultiByteToWideChar of unicode.cpp. If I understand correctly, the only difference is that in MultiByteToWideChar, it makes source length and destination length one byte longer than it is. Doing this in mono won't stop this check ((sourceIndex < sourceLength) && (destinationIndex >= destinationLength)) either.
There was a problem hiding this comment.
@fanyang-mono, coreclr ultimately uses the same implementation (couple of wrappers later), but in reflection stack (metasig.h etc.), there it compensates for those off-by-one, zero-length kind of scenarios to match the expectation.
There was a problem hiding this comment.
@am11 Do you mind pointing me to the CoreCLR source code where they handles off-by-one, zero-length kind of scenarios?
There was a problem hiding this comment.
@fanyang-mono, good question. 😅 It is a bit tricky to discern the exact place without actually attaching a conditional debugger (or some tricky way), but I was debugging the similar tests for coreclr last year #85558 (comment) which points to
One thing I do remember; when all PAL tests were passing and 99% of managed tests were passingm those edge cases covered in reflection tests were still failing, which is why I had to add a similar special condition in unicode.cpp -> utf8.c conversion to balance varied expectations of mono and coreclr + reflection simultaneously.
There was a problem hiding this comment.
It is probably fine having this a little bit longer check, because to make mono align with CoreCLR on this is non-trivial work.
AaronRobinsonMSFT
left a comment
There was a problem hiding this comment.
Thanks. I will take a look.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Co-authored-by: Aaron Robinson <arobins@microsoft.com>
jkotas
commented
May 22, 2024
MultiByteToWideChar: 220ms per iteration |
Ran some google benchmarks: https://gist.github.com/am11/8d949f6c112aaab1575e85f2f350c59e Long length: 140 main: PR:
// Use vectorized operations for ASCII characterstypedefuint16_tv4ui __attribute__ ((vector_size (8)));
for (; sourceIndex<sourceLength&&destinationIndex<destinationLength; sourceIndex+=4, destinationIndex+=4)
{
v4uidata=*(v4ui*)&source[sourceIndex];
v4uimask4= (v4ui){0x007F, 0x007F, 0x007F, 0x007F};
v4uicomparison=data <= mask4;
size_tremainingLength=sourceLength-sourceIndex;
if (remainingLength>4&&__builtin_expect(comparison[0] &comparison[1] &comparison[2] &comparison[3], 1))
{
destination[destinationIndex] = (char)data[0];
destination[destinationIndex+1] = (char)data[1];
destination[destinationIndex+2] = (char)data[2];
destination[destinationIndex+3] = (char)data[3];
continue;
}
elseif (remainingLength <= 4)
{
// Handle the remaining 4 or less elements if ASCIIif (remainingLength==4) {
if (__builtin_expect(comparison[0] &comparison[1] &comparison[2] &comparison[3], 1))
{
destination[destinationIndex++] = (char)data[0];
destination[destinationIndex++] = (char)data[1];
destination[destinationIndex++] = (char)data[2];
destination[destinationIndex++] = (char)data[3];
returndestinationIndex;
}
break;
}
v4uimask3= (v4ui){0x007F, 0x007F, 0x007F};
v4uimask2= (v4ui){0x007F, 0x007F};
comparison=data <= mask3;
if (remainingLength==3) {
if( __builtin_expect(comparison[0] &comparison[1] &comparison[2], 1)) {
destination[destinationIndex++] = (char)data[0];
destination[destinationIndex++] = (char)data[1];
destination[destinationIndex++] = (char)data[2];
returndestinationIndex;
}
break;
}
comparison=data <= mask2;
if (remainingLength==2) {
if(__builtin_expect(comparison[0] &comparison[1], 1)) {
destination[destinationIndex++] = (char)data[0];
destination[destinationIndex++] = (char)data[1];
returndestinationIndex;
}
break;
}
if (source[sourceIndex] <0x80){
destination[destinationIndex++] = (char)source[sourceIndex];
returndestinationIndex;
}
}
// Break out of the outer loop here because we know it's non-ASCIIbreak;
}
// Handle non-ASCII and mixed caseswhile (sourceIndex<sourceLength&&destinationIndex<destinationLength)
{
...but that did not move the needle (+/- 2ns diff). Haven't given up on fine-tuning yet, but overall I think the new implementation is simple and more readable. So it's a balancing question of readability vs. performance (I'm pursuing both 🙂). |
AaronRobinsonMSFT
commented
Jul 2, 2024
@am11 I'm moving this to draft for now. |
Draft Pull Request was automatically closed for 30 days of inactivity. Please let us know if you'd like to reopen it. |
diet version 🤸