From b74243c37c747e8eab815125121f0307e234df8a Mon Sep 17 00:00:00 2001 From: Paul Anderson Date: Tue, 26 May 2026 11:20:29 +0530 Subject: [PATCH] Perf: Eliminate LINQ allocations in BoxAndWhiskerSeries.GenerateSegments Replace per-iteration LINQ .Where().ToArray() with a manual loop-based FilterNaNValues helper that avoids closure and enumerator allocations. Replace .Average() with a manual sum loop to eliminate LINQ overhead. Replace .Min()/.Max() on an already-sorted array with direct index access (yList[0] and yList[^1]). These changes reduce GC pressure in the hot path that runs once per data point during chart segment generation. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- maui/src/Charts/Series/BoxAndWhiskerSeries.cs | 41 +++++++++++++++++-- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/maui/src/Charts/Series/BoxAndWhiskerSeries.cs b/maui/src/Charts/Series/BoxAndWhiskerSeries.cs index ad2fbfbf..1462b769 100644 --- a/maui/src/Charts/Series/BoxAndWhiskerSeries.cs +++ b/maui/src/Charts/Series/BoxAndWhiskerSeries.cs @@ -1034,14 +1034,20 @@ internal override void GenerateSegments(SeriesView seriesView) for (int i = 0; i < PointsCount; i++) { - var yList = YDataCollection[i].Where(x => !double.IsNaN(x)).ToArray(); + var yList = FilterNaNValues(YDataCollection[i]); List outliers = []; if (yList.Length > 0) { Array.Sort(yList); - average = yList.Average(); + double sum = 0; + for (int j = 0; j < yList.Length; j++) + { + sum += yList[j]; + } + + average = sum / yList.Length; } int yCount = yList.Length; @@ -1086,8 +1092,8 @@ internal override void GenerateSegments(SeriesView seriesView) } else { - minimum = yList.Min(); - maximum = yList.Max(); + minimum = yList[0]; + maximum = yList[^1]; } double actualMinimum = minimum; @@ -1487,6 +1493,33 @@ static void GetMinMaxandOutlier(double lowerQuartile, double upperQuartile, doub } } + /// + /// Filters out NaN values from the source list without LINQ allocations. + /// + static double[] FilterNaNValues(IList source) + { + int count = 0; + for (int i = 0; i < source.Count; i++) + { + if (!double.IsNaN(source[i])) + { + count++; + } + } + + var result = new double[count]; + int index = 0; + for (int i = 0; i < source.Count; i++) + { + if (!double.IsNaN(source[i])) + { + result[index++] = source[i]; + } + } + + return result; + } + void GetQuartileValues(double[] yList, int len, out double lowerQuartile, out double upperQuartile) { double[] lowerQuartileArray;