-
Notifications
You must be signed in to change notification settings - Fork 5.2k
Description
I'm sorting several thousand strings in my App (Windows, Android, iOS), but on iOS it is SIGNIFICANTLY slower than on other platforms. (not running NativeAOT on iOS yet)
source.OrderBy(item => GetNaturalSortKey(keySelector(item)), StringComparer.CurrentCultureIgnoreCase) - for the same data set is taking several ms/ tens ms, on iOS it's taking more than 600ms. Delays in UI etc. on iOS. So we had to #if def on iOS platform for own written natural sort which is faster (on iOS) that simple linq.
I think it's similar to #112123
Maybe things like getting current thread culture for comparison on iOS is taking too long or something like that?
I think it's a regression because with Xamarin.iOS (not sure about net7-ios) we had no issues with performance with part of code.
Reproducible on net9-ios, net10-ios (rc2). Not reproducible on Windows or Android (net5-10)
public static IOrderedEnumerable<T> OrderByNatural<T>(this IEnumerable<T> source, Func<T, string> keySelector)
{
#if IOS
return source.OrderBy(item => GetNaturalSortKey(keySelector(item)), StringComparer.CurrentCultureIgnoreCase);
#else
return source.OrderBy(keySelector, new CustomComparer<string>(NaturalComparer.Compare));
#endif
}
#if IOS
private static readonly StringBuilder stringBuilderName = new();
private static readonly StringBuilder stringBuilderNumber = new();
private static string GetNaturalSortKey(string name)
{
if (string.IsNullOrEmpty(name))
return string.Empty;
// Fast path: if no digits, return original string
bool hasDigits = false;
for (int i = 0; i < name.Length; i++)
{
if (char.IsDigit(name[i]))
{
hasDigits = true;
break;
}
}
if (!hasDigits)
return name;
stringBuilderName.Clear();
stringBuilderNumber.Clear();
foreach (char c in name)
{
if (char.IsDigit(c))
{
stringBuilderNumber.Append(c);
}
else
{
if (stringBuilderNumber.Length > 0)
{
// Pad numbers to 10 digits so they sort naturally
stringBuilderName.Append(stringBuilderNumber.ToString().PadLeft(10, '0'));
stringBuilderNumber.Clear();
}
stringBuilderName.Append(c);
}
}
if (stringBuilderNumber.Length > 0)
{
stringBuilderName.Append(stringBuilderNumber.ToString().PadLeft(10, '0'));
}
return stringBuilderName.ToString();
}
#endif