Skip to content

Commit cd4c7cf

Browse files
[STJ] Optimize EnumFieldInfo sorting (#117839)
* [STJ] Only compute PopCount once when topologically sorting Enums Packs the calculated (once per entry) PopCount into the high 32 bits of the long and the original index into the low 32 bits. Then that can just be sorted using the heavily optimized Array.Sort() method. After sorting just extract the low 32 bits as the original array index. As before, we negate the actual _PopCount_ to ensure that `Key`s with more on-bits (e.g. more flags represented) will sort **first**. This trades 2 x O(N log N) [average case] to 2 x O(N^2) [worst case] calls to the `popcount` instruction (or the emulation if NET is not defined) for N **shift-left-32** and **or** + N x **truncate to 32 bits**. It _also_ eliminates the overhead of the `CompareTo` method as it's now a direct `long` low-level compare. * PR Feedback Switched to Tuple * PR Feedback Switched to native tuple syntax Co-authored-by: Pranav Senthilnathan <[email protected]> * Update src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/EnumConverter.cs --------- Co-authored-by: Eirik Tsarpalis <[email protected]>
1 parent 42a2f25 commit cd4c7cf

File tree

1 file changed

+19
-23
lines changed
  • src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value

1 file changed

+19
-23
lines changed

src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/EnumConverter.cs

Lines changed: 19 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -676,44 +676,40 @@ private static EnumFieldInfo[] TopologicalSortEnumFields(EnumFieldInfo[] enumFie
676676
return enumFields;
677677
}
678678

679-
var indices = new int[enumFields.Length];
679+
var indices = new (int negativePopCount, int index)[enumFields.Length];
680680
for (int i = 0; i < enumFields.Length; i++)
681681
{
682-
indices[i] = i;
682+
// We want values with more bits set to come first so negate the pop count.
683+
// Keep the index as a second comparand so that sorting stability is preserved.
684+
indices[i] = (-PopCount(enumFields[i].Key), i);
683685
}
684686

685-
Array.Sort(indices, (i, j) => GetComparisonKey(i).CompareTo(GetComparisonKey(j)));
687+
Array.Sort(indices);
686688

687689
var sortedFields = new EnumFieldInfo[enumFields.Length];
688690
for (int i = 0; i < indices.Length; i++)
689691
{
690-
sortedFields[i] = enumFields[indices[i]];
692+
// extract the index from the sorted tuple
693+
int index = indices[i].index;
694+
sortedFields[i] = enumFields[index];
691695
}
692696

693697
return sortedFields;
698+
}
694699

695-
(int PopCount, int Index) GetComparisonKey(int i)
696-
{
697-
// Sort by descending pop count of the enum value.
698-
// Since Array.Sort isn't a stable algorithm
699-
// append the current index to the comparison key.
700-
return (-PopCount(enumFields[i].Key), i);
701-
}
702-
703-
static int PopCount(ulong value)
704-
{
700+
private static int PopCount(ulong value)
701+
{
705702
#if NET
706-
return (int)ulong.PopCount(value);
703+
return (int)ulong.PopCount(value);
707704
#else
708-
int count = 0;
709-
while (value != 0)
710-
{
711-
value &= value - 1;
712-
count++;
713-
}
714-
return count;
715-
#endif
705+
int count = 0;
706+
while (value != 0)
707+
{
708+
value &= value - 1;
709+
count++;
716710
}
711+
return count;
712+
#endif
717713
}
718714

719715
private enum EnumFieldNameKind

0 commit comments

Comments
 (0)