Skip to content

[iOS] Slow string sorting compared to other platforms #121204

@michaldobrodenka

Description

@michaldobrodenka

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

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions