Skip to content

Commit efaf542

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 ad65c67 commit efaf542

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
@@ -685,44 +685,40 @@ private static EnumFieldInfo[] TopologicalSortEnumFields(EnumFieldInfo[] enumFie
685685
return enumFields;
686686
}
687687

688-
var indices = new int[enumFields.Length];
688+
var indices = new (int negativePopCount, int index)[enumFields.Length];
689689
for (int i = 0; i < enumFields.Length; i++)
690690
{
691-
indices[i] = i;
691+
// We want values with more bits set to come first so negate the pop count.
692+
// Keep the index as a second comparand so that sorting stability is preserved.
693+
indices[i] = (-PopCount(enumFields[i].Key), i);
692694
}
693695

694-
Array.Sort(indices, (i, j) => GetComparisonKey(i).CompareTo(GetComparisonKey(j)));
696+
Array.Sort(indices);
695697

696698
var sortedFields = new EnumFieldInfo[enumFields.Length];
697699
for (int i = 0; i < indices.Length; i++)
698700
{
699-
sortedFields[i] = enumFields[indices[i]];
701+
// extract the index from the sorted tuple
702+
int index = indices[i].index;
703+
sortedFields[i] = enumFields[index];
700704
}
701705

702706
return sortedFields;
707+
}
703708

704-
(int PopCount, int Index) GetComparisonKey(int i)
705-
{
706-
// Sort by descending pop count of the enum value.
707-
// Since Array.Sort isn't a stable algorithm
708-
// append the current index to the comparison key.
709-
return (-PopCount(enumFields[i].Key), i);
710-
}
711-
712-
static int PopCount(ulong value)
713-
{
709+
private static int PopCount(ulong value)
710+
{
714711
#if NET
715-
return (int)ulong.PopCount(value);
712+
return (int)ulong.PopCount(value);
716713
#else
717-
int count = 0;
718-
while (value != 0)
719-
{
720-
value &= value - 1;
721-
count++;
722-
}
723-
return count;
724-
#endif
714+
int count = 0;
715+
while (value != 0)
716+
{
717+
value &= value - 1;
718+
count++;
725719
}
720+
return count;
721+
#endif
726722
}
727723

728724
private enum EnumFieldNameKind

0 commit comments

Comments
 (0)