Skip to content

perf: Reduce allocations and eliminate delegate overhead across 5 controls#385

Open
PaulAndersonS wants to merge 1 commit into
mainfrom
paulandersons/curly-winner
Open

perf: Reduce allocations and eliminate delegate overhead across 5 controls#385
PaulAndersonS wants to merge 1 commit into
mainfrom
paulandersons/curly-winner

Conversation

@PaulAndersonS
Copy link
Copy Markdown
Collaborator

Root Cause of the Issue

Multiple controls perform unnecessary memory allocations and redundant work in frequently-called paths:

  • LINQ queries that allocate intermediate collections just to find a single element
  • Capturing lambdas allocated on every iteration of render loops
  • Missing event handler unsubscription leading to handler accumulation
  • Lists created without capacity hints in per-frame rendering methods
  • LINQ Select + Contains chains creating iterator objects for simple lookups

Description of Change

Five targeted performance improvements across 5 files:

  1. CartesianAxisLayout.csGetAxisByName() used a LINQ query expression with .ToList() just to get the first matching axis. Replaced with a foreach loop that returns on first match, eliminating the list allocation entirely.

  2. Calendar MonthView.cs_specialDates.FirstOrDefault(details => ...) was called once per calendar cell (28-42 times per render frame), each time allocating a new delegate that captures the changing dateTime local. Extracted to a GetSpecialDateIcon() method using an indexed for loop.

  3. ChartSeries.csDataLabelSettings_PropertyChanged subscribed to LabelStyle.PropertyChanged without first unsubscribing, causing duplicate handlers to accumulate each time LabelStyle was reassigned. Added the missing -= before +=.

  4. PolarAreaSegment.csGenerateInteriorPoints() and GenerateStrokePoints() created new List<float>() with default capacity (called every render frame). Pre-allocated with calculated capacity (_pointsCount + 2) * 2 to avoid repeated array resizing.

  5. SfTimePicker.cs.Select(m => int.Parse(m)).Contains(value) allocates an iterator, a delegate, and evaluates lazily. Replaced with a static ContainsParsedValue() helper using a for loop with int.TryParse — zero allocations.

Issues Fixed

Performance optimization — no specific issue number.

Screenshots

N/A — internal allocation/performance changes with no visual impact.

…trols

1. CartesianAxisLayout.GetAxisByName — Replace LINQ query + ToList() with
   simple foreach loop, eliminating list allocation just to get first match.

2. Calendar MonthView special dates lookup — Replace FirstOrDefault() with
   capturing lambda (allocated per cell) with indexed for loop, avoiding
   delegate allocation on each of 28-42 cells per render frame.

3. ChartSeries DataLabelSettings — Add missing unsubscribe before resubscribe
   on LabelStyle.PropertyChanged, preventing duplicate handler accumulation
   and potential memory leak when LabelStyle is reassigned multiple times.

4. PolarAreaSegment — Pre-allocate List<float> with calculated capacity in
   GenerateInteriorPoints/GenerateStrokePoints, avoiding repeated resizing
   during each render frame.

5. SfTimePicker — Replace LINQ Select(int.Parse).Contains() pattern with
   simple for loop using int.TryParse, eliminating iterator allocation and
   intermediate sequence materialization during column selection changes.

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