Улучшена производительность расчета скользящего среднего. IEnumerable против списка, foreach против for, ElementAt,C#

Место общения программистов C#
Ответить
Anonymous
 Улучшена производительность расчета скользящего среднего. IEnumerable против списка, foreach против for, ElementAt,

Сообщение Anonymous »

Я создал класс для расчета экспоненциальной скользящей средней:

Код: Выделить всё

public class ExponentialMovingAverage {

public Int32 Period { get; set; }

public ExponentialMovingAverage(Int32 period = 20) {

ArgumentOutOfRangeException.ThrowIfNegativeOrZero(period);

Period = period;

}

public override IEnumerable Compute(IEnumerable inputs) {

ArgumentNullException.ThrowIfNull(inputs);

inputs = inputs.OrderBy(x => x.Stamp);

Decimal? previous = null;

Decimal factor = (Decimal)(2d / (Period + 1));

Decimal? sum = 0;

Int32 notNulls = 0;

for (Int32 index = 0; index < inputs.Count(); index++) {

(DateTimeOffset stamp, Decimal? value) = inputs.ElementAt(index);

if (value == null) {
notNulls++;
yield return (stamp, null);
continue;
}

if (index < notNulls + Period - 1) {
sum += value;
yield return (stamp, null);
continue;
}

if (index == notNulls + Period - 1) {
sum += value;
Decimal? sma = sum / Period;
previous = sma;
yield return (stamp, sma);
continue;
}

Decimal? ema = previous + (factor * (value - previous));
previous = ema;
yield return (stamp, ema);

}

}

}
И у меня есть следующие тесты, которые проходят:

Код: Выделить всё

[Fact]
public void Test_AllNonNullInputs() {
var ema = new ExponentialMovingAverage(3);

var inputs = new List {
(new DateTimeOffset(2024, 1, 1, 0, 0, 0, TimeSpan.Zero), 10),
(new DateTimeOffset(2024, 1, 2, 0, 0, 0, TimeSpan.Zero), 15),
(new DateTimeOffset(2024, 1, 3, 0, 0, 0, TimeSpan.Zero), 20),
(new DateTimeOffset(2024, 1, 4, 0, 0, 0, TimeSpan.Zero), 25),
(new DateTimeOffset(2024, 1, 5, 0, 0, 0, TimeSpan.Zero), 30),
(new DateTimeOffset(2024, 1, 6, 0, 0, 0, TimeSpan.Zero), 35)
};
var output = ema.Compute(inputs).ToList();

Assert.Equal(6, output.Count);
Assert.Null(output[0].ExponentialMovingAverage);
Assert.Null(output[1].ExponentialMovingAverage);
Assert.Equal(15m, output[2].ExponentialMovingAverage);
Assert.Equal(20m, output[3].ExponentialMovingAverage);
Assert.Equal(25m, output[4].ExponentialMovingAverage);
Assert.Equal(30m, output[5].ExponentialMovingAverage);
}

[Fact]
public void Test_FirstTwoInputsAreNull() {
var ema = new ExponentialMovingAverage(3);

var inputs = new List {
(new DateTimeOffset(2024, 1, 1, 0, 0, 0, TimeSpan.Zero), null),
(new DateTimeOffset(2024, 1, 2, 0, 0, 0, TimeSpan.Zero), null),
(new DateTimeOffset(2024, 1, 3, 0, 0, 0, TimeSpan.Zero), 20),
(new DateTimeOffset(2024, 1, 4, 0, 0, 0, TimeSpan.Zero), 25),
(new DateTimeOffset(2024, 1, 5, 0, 0, 0, TimeSpan.Zero), 30),
(new DateTimeOffset(2024, 1, 6, 0, 0, 0, TimeSpan.Zero), 35)
};
var output = ema.Compute(inputs).ToList();

Assert.Equal(6, output.Count);
Assert.Null(output[0].ExponentialMovingAverage);
Assert.Null(output[1].ExponentialMovingAverage);
Assert.Null(output[2].ExponentialMovingAverage);
Assert.Null(output[3].ExponentialMovingAverage);
Assert.Equal(25m, output[4].ExponentialMovingAverage);
Assert.Equal(30m, output[5].ExponentialMovingAverage);
}
Вычисление кажется медленным, например, при расчете EMA для EMA со многими входными данными:

Код: Выделить всё

var ema1 = new ExponentialMovingAverage(3);
var outputs1 = ema1.Compute(inputs);
var ema2 = new ExponentialMovingAverage(3);
var outputs2 = ema2.Compute(ema1);
Я рассматривал использование inputs.ElementAt(index), а также использование foreach vs for и List vs Enumerable.
Как я могу улучшить код, в том числе его производительность?

Подробнее здесь: https://stackoverflow.com/questions/784 ... st-foreach
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «C#»