Skip to content

perf: Reduce allocations and redundant enumerations across Carousel, NumericEntry, Calendar, and BottomSheet#383

Open
PaulAndersonS wants to merge 1 commit into
mainfrom
paulandersons/verbose-enigma
Open

perf: Reduce allocations and redundant enumerations across Carousel, NumericEntry, Calendar, and BottomSheet#383
PaulAndersonS wants to merge 1 commit into
mainfrom
paulandersons/verbose-enigma

Conversation

@PaulAndersonS
Copy link
Copy Markdown
Collaborator

Root Cause of the Issue

Multiple controls perform redundant work in frequently-called paths — repeated IEnumerable enumeration, per-character string allocations, per-instance collection allocations for defaults, and double dictionary lookups — all of which create unnecessary GC pressure.

Description of Change

Five targeted performance improvements across 4 controls:

  1. Carousel (iOS/Windows/Android)MapSelectedIndex/UpdateSelectedIndex called .Count() up to 3 times and .Any() once on IEnumerable<object> ItemsSource. Each call fully enumerates the collection. Fixed by caching the count in a local variable.

  2. NumericEntryGetMaximumFractionDigit built a digit string character-by-character using += (O(n²) allocations). Since the format suffix is at most 2 characters, replaced with a single int.TryParse() call that avoids all intermediate string allocations.

  3. Calendar ViewChangedEventArgs — Each instance allocated new List<DateTime>().AsReadOnly() twice for default property values (2 List + 2 ReadOnlyCollection objects per event). Replaced with a shared static readonly empty collection.

  4. Calendar CalendarViewHelper.GetCurrentMonthDatesnew List<DateTime>() without capacity hint caused repeated internal array resizing. Pre-allocated with visibleDates.Count as the upper bound.

  5. BottomSheetContainsKey + indexer SET pattern performs two hash lookups. Replaced with TryAdd() which performs a single lookup.

Issues Fixed

N/A — proactive performance improvement

Unit Tests Added

  • CustomFormat_StandardFormatWithDigits_ParsesMaxFractionCorrectly — Verifies standard format specifiers (C2, C10, N0, F99) are parsed correctly after the int.TryParse optimization
  • CustomFormat_CustomFormatWithNonDigitSuffix_FallsBackToCustomParsing — Verifies non-numeric suffixes fall back to custom format parsing
  • CalendarViewChangedEventArgs_DefaultProperties_UseSharedEmptyCollection — Verifies the static empty collection is shared across instances (no per-instance allocation)
  • CalendarViewChangedEventArgs_AssignedDates_ReturnsCorrectValues — Verifies assigned dates work correctly
  • SelectedIndex_WithItemsSource_ClampsCorrectly — Verifies carousel index clamping with the cached count approach

Screenshots

N/A — no visual changes

1. Carousel (iOS/Windows/Android): Cache ItemsSource.Count() to avoid
   repeated IEnumerable enumeration in MapSelectedIndex/UpdateSelectedIndex.

2. NumericEntry: Replace string concatenation loop with direct int.TryParse
   on the format suffix, eliminating per-char ToString() allocations.

3. Calendar ViewChangedEventArgs: Use a shared static empty ReadOnlyCollection
   instead of allocating new List + AsReadOnly per event args instance.

4. Calendar CalendarViewHelper: Pre-allocate List<DateTime> with known capacity
   in GetCurrentMonthDates to avoid intermediate resizing.

5. BottomSheet: Replace ContainsKey + indexer SET with TryAdd to eliminate
   redundant dictionary hash lookups.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant