From 81830c9031bc8198b2a09dcdec85e39fc1784046 Mon Sep 17 00:00:00 2001 From: Miha Kralj Date: Wed, 10 Dec 2025 15:15:20 -0500 Subject: [PATCH] Optimize memory allocation in ALMA and Conv classes; improve performance by using stackalloc for small periods --- lib/trends/alma/Alma.cs | 4 ++-- lib/trends/conv/Conv.cs | 15 +++++---------- lib/trends/wma/Wma.cs | 4 +--- 3 files changed, 8 insertions(+), 15 deletions(-) diff --git a/lib/trends/alma/Alma.cs b/lib/trends/alma/Alma.cs index 5f78de16..a94eb6d5 100644 --- a/lib/trends/alma/Alma.cs +++ b/lib/trends/alma/Alma.cs @@ -210,7 +210,8 @@ public sealed class Alma : ITValuePublisher throw new ArgumentException("Source and output must have the same length"); // Precompute weights - double[] weights = new double[period]; + // Use stackalloc for small periods to avoid heap allocation + Span weights = period <= 256 ? stackalloc double[period] : new double[period]; double m = offset * (period - 1); double s = period / sigma; double s2 = 2 * s * s; @@ -224,7 +225,6 @@ public sealed class Alma : ITValuePublisher } // Buffer for sliding window - // Use stackalloc for small periods Span buffer = period <= 256 ? stackalloc double[period] : new double[period]; int bufferIdx = 0; int count = 0; diff --git a/lib/trends/conv/Conv.cs b/lib/trends/conv/Conv.cs index c72d9fe6..9739b642 100644 --- a/lib/trends/conv/Conv.cs +++ b/lib/trends/conv/Conv.cs @@ -26,7 +26,6 @@ public sealed class Conv : ITValuePublisher private readonly RingBuffer _buffer; private double _lastValidValue; - private int _head; // State for bar correction private double _p_lastValidValue; @@ -83,7 +82,6 @@ public sealed class Conv : ITValuePublisher if (isNew) { _buffer.Add(val); - _head = (_head + 1 == _period) ? 0 : _head + 1; } else { @@ -104,10 +102,11 @@ public sealed class Conv : ITValuePublisher } else { - // Full: data is split at _head (which points to oldest) - int part1Len = _period - _head; - result = internalBuf.Slice(_head, part1Len).DotProduct(kernelSpan.Slice(0, part1Len)) - + internalBuf.Slice(0, _head).DotProduct(kernelSpan.Slice(part1Len)); + // Full: data is split at StartIndex (which points to oldest) + int head = _buffer.StartIndex; + int part1Len = _period - head; + result = internalBuf.Slice(head, part1Len).DotProduct(kernelSpan.Slice(0, part1Len)) + + internalBuf.Slice(0, head).DotProduct(kernelSpan.Slice(part1Len)); } } @@ -165,9 +164,6 @@ public sealed class Conv : ITValuePublisher _buffer.Add(val); } - // Sync _head with buffer state - _head = windowSize % _period; - // Set Last Last = new TValue(source.Times[len - 1], vSpan[len - 1]); @@ -247,7 +243,6 @@ public sealed class Conv : ITValuePublisher _buffer.Clear(); _lastValidValue = double.NaN; _p_lastValidValue = double.NaN; - _head = 0; Last = default; } } diff --git a/lib/trends/wma/Wma.cs b/lib/trends/wma/Wma.cs index 5e8b6bd9..2b83fe68 100644 --- a/lib/trends/wma/Wma.cs +++ b/lib/trends/wma/Wma.cs @@ -421,9 +421,7 @@ public sealed class Wma : ITValuePublisher var vSums = Avx.Add(vSumState, vPS2); - var vSumsShifted = Avx2.Permute4x64(vSums.AsUInt64(), 0b_10_01_00_00).AsDouble(); // skipcq: CS-R1131 - vSumsShifted = Avx.Blend(vSumState, vSumsShifted, 0b_1110); - + var vSumsShifted = Avx.Subtract(vSums, vDeltaS); var vTerm1 = Avx.Multiply(vPeriod, vNew); var vU = Avx.Subtract(vTerm1, vSumsShifted);