-
Notifications
You must be signed in to change notification settings - Fork 5.2k
Pack OrderBy ThenBy keys in LINQ #120786
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
base: main
Are you sure you want to change the base?
Pack OrderBy ThenBy keys in LINQ #120786
Conversation
| private readonly bool _descending; | ||
| private readonly EnumerableSorter<TElement>? _next; | ||
| private TKey[]? _keys; | ||
| private readonly byte _packingUsedSize; |
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 purpose of this field is saving the bytes used for the packing
For example: If you pack an int and a bool, only 5 bytes will be used
and the packing will use the ulong (8 bytes) type to accommodate everything
And still have 3 bytes left for some other type
| uint toggle = 0U; | ||
| if (key1typeIsSigned) | ||
| { | ||
| toggle |= 1U << ((totalSize * 8) - 1); |
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 makes the toggle for the sign bit:
1U << ((sizeof(sbyte) * 8) - 1)
would be in binary:
00000000_00000000_00000000_10000000
|
Tagging subscribers to this area: @dotnet/area-system-linq |
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.
Generally, I don't think it's a good direction for optimization. It's increasing usage of unsafe code, which can also violate other implications like endianness and alignment. It hasn't been validated with potential trimming issues, either.
In the other hand, users can easily achieve same optimization manually with something like .OrderBy(x => (long)x.Prop1 << 32) | (x.Prop2)).
| // WriteUnaligned will write the bits in the low end of result | ||
| Unsafe.WriteUnaligned(ref Unsafe.As<TNewKey, byte>(ref result), lowKey); | ||
| // result = 00000000_01111000 | ||
| // |--lo--| | ||
|
|
||
| // now we want to skip the size of the lowKey writted in the result | ||
| ref byte dest = ref Unsafe.Add(ref Unsafe.As<TNewKey, byte>(ref result), key2typeSize); | ||
| // |--hi--| |--lo--| | ||
| // 00000000_01111000 | ||
| // ^ now we are here | ||
|
|
||
| // Write the highKey after lowKey | ||
| Unsafe.WriteUnaligned(ref dest, highKey); |
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 has an implication about endianness. It will get incorrect result on big endian machine.
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.
Thanks, but I don't think this is a change we would want to pursue.
I agree about the unsafe code, but for this optimization it seems that is no other option, I tried writing without unsafe code but I had to fight a lot against generics and type safety, it's almost impossible to write it without unsafe code unless we actualy make a specialized implementation for every type, and because it's almost 2x performance I think is justifiable the use of unsafe
As far as I know,
That's true, I forgot about big-endian
I could be wrong, but doesn't
I'm not very familiar with this type of issue, can you explain exactly what might cause the necessary code to be accidentally trimmed? Is there still a chance this will make it into Linq? If so, I can add the big endian implementation |
Fixes #120785
Benchmark: