Skip to content

perf: Reduce allocations and eliminate boxing across ProgressBar, SparkChart, Picker, SegmentedControl, and EffectsView#387

Open
PaulAndersonS wants to merge 1 commit into
mainfrom
paulandersons/shiny-garbanzo
Open

perf: Reduce allocations and eliminate boxing across ProgressBar, SparkChart, Picker, SegmentedControl, and EffectsView#387
PaulAndersonS wants to merge 1 commit into
mainfrom
paulandersons/shiny-garbanzo

Conversation

@PaulAndersonS
Copy link
Copy Markdown
Collaborator

Root Cause of the Issue

Multiple controls have unnecessary heap allocations and CPU overhead in hot paths (rendering, layout, touch events) due to:

  • LINQ methods (OrderBy().ToList(), Distinct().ToList(), First(), Last()) creating intermediate collections
  • IEnumerable<Enum> return types causing boxing of value-type enums
  • string.ToString() comparisons instead of direct enum equality checks
  • OfType<T>().FirstOrDefault() and Cast<T>() creating unnecessary enumerator wrappers

Description of Change

5 targeted performance improvements:

  1. ProgressBar Utility.UpdateGradientStopCollection — Replace OrderBy().ToList() with in-place List.Sort() and cache First()/Last() as direct [0]/[Count-1] indexer access. Eliminates LINQ allocation + 4 redundant enumerations.

  2. SparkAreaChart OnDraw — Cache segment[0], segment[Count-1], and Count before the inner loop. Eliminates repeated First()/Last() calls in the per-frame rendering hot path.

  3. DatePickerHelper & TimePickerHelper — Replace Distinct().ToList() with HashSet<int>-based RemoveAll(). Deduplicates in-place without allocating a new list.

  4. SegmentedControl SegmentLayout — Replace Children.OfType<T>().FirstOrDefault() and Children.Cast<T>() with direct foreach+type-check loops with early exit. Avoids enumerator allocations.

  5. EffectsView Utils.cs — Change GetAllItems() / GetAllAutoResetEffectsItems() return types from IEnumerable<Enum> to IEnumerable<SfEffects> / IEnumerable<AutoResetEffects>. Eliminates boxing, removes .ToString() comparisons (replaced with direct enum equality), and removes .Select(v => (T)v) cast wrappers at call sites.

Issues Fixed

N/A — Proactive performance improvement (no user-reported issue)

Unit Tests Added

  • ProgressBarUtilityUnitTests — 5 tests verifying sort order, boundary insertion at start/end, and no-op when range is covered
  • EffectsViewUtilsUnitTests — 6 tests verifying flag decomposition for None, single flags, and combined flags for both SfEffects and AutoResetEffects

Screenshots

N/A — No visual changes

1. ProgressBar Utility: Replace OrderBy().ToList() with in-place Sort() and
   cache First()/Last() lookups as direct indexer access, eliminating LINQ
   allocations in gradient stop processing.

2. SparkAreaChart: Cache segment[0], segment[Count-1], and Count in OnDraw
   loop to avoid repeated First()/Last() enumerations during rendering.

3. DatePicker/TimePicker: Replace Distinct().ToList() with HashSet-based
   RemoveAll() to eliminate intermediate list allocations during format
   order deduplication.

4. SegmentedControl: Replace OfType<T>().FirstOrDefault() and Cast<T>()
   LINQ calls with direct type-check loops for early-exit selection lookup.

5. EffectsView: Change GetAllItems()/GetAllAutoResetEffectsItems() return
   types from IEnumerable<Enum> to typed IEnumerable<SfEffects> and
   IEnumerable<AutoResetEffects>, eliminating boxing, string comparisons,
   and Select() cast wrappers in touch event hot paths.

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