chore: initial project import
This commit is contained in:
commit
3fc4e5ca53
347 files changed
+56172
No files matched your search
@@ -0,0 +1,241 @@
|
||||
namespace Monitor.Backend.Analysis;
|
||||
|
||||
public enum AccKind : byte
|
||||
{
|
||||
Counter = 1,
|
||||
Ratio = 2,
|
||||
Sum = 3,
|
||||
Min = 4,
|
||||
Max = 5,
|
||||
Histogram = 6,
|
||||
RunLength = 7,
|
||||
Series = 8,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 所有指标的中间状态只能是下面几种累加器之一。合并规则只写一次,
|
||||
/// 存储、降采样、按时间范围聚合都通用处理。
|
||||
/// </summary>
|
||||
public abstract class Accumulator
|
||||
{
|
||||
public abstract AccKind Kind { get; }
|
||||
|
||||
/// <summary>加入一个观测值。含义由累加器决定:Counter 加 <paramref name="a"/>,Ratio 的 (a, b) 是 (分子, 分母),Histogram 记录 a。</summary>
|
||||
public abstract void Add(double a, double b = 0);
|
||||
|
||||
/// <summary>合并同类型的另一个累加器(精确合并,不是平均)。</summary>
|
||||
public abstract void Merge(Accumulator other);
|
||||
|
||||
public abstract Accumulator Clone();
|
||||
|
||||
/// <summary>样本数,用于置信说明。</summary>
|
||||
public abstract ulong SampleCount { get; }
|
||||
|
||||
public static Accumulator Create(AccKind kind) => kind switch
|
||||
{
|
||||
AccKind.Counter => new CounterAcc(),
|
||||
AccKind.Ratio => new RatioAcc(),
|
||||
AccKind.Sum => new SumAcc(),
|
||||
AccKind.Min => new MinAcc(),
|
||||
AccKind.Max => new MaxAcc(),
|
||||
AccKind.Histogram => new HistogramAcc(),
|
||||
AccKind.RunLength => new RunLengthAcc(),
|
||||
AccKind.Series => new SeriesAcc(),
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(kind)),
|
||||
};
|
||||
|
||||
protected T Cast<T>(Accumulator other)
|
||||
where T : Accumulator =>
|
||||
other as T ?? throw new InvalidOperationException($"cannot merge {other.Kind} into {Kind}");
|
||||
}
|
||||
|
||||
public sealed class CounterAcc : Accumulator
|
||||
{
|
||||
public ulong Value { get; set; }
|
||||
|
||||
public override AccKind Kind => AccKind.Counter;
|
||||
|
||||
public override ulong SampleCount => Value;
|
||||
|
||||
public override void Add(double a, double b = 0) => Value += (ulong)Math.Max(0, a);
|
||||
|
||||
public override void Merge(Accumulator other) => Value += Cast<CounterAcc>(other).Value;
|
||||
|
||||
public override Accumulator Clone() => new CounterAcc { Value = Value };
|
||||
}
|
||||
|
||||
public sealed class RatioAcc : Accumulator
|
||||
{
|
||||
public ulong Numerator { get; set; }
|
||||
|
||||
public ulong Denominator { get; set; }
|
||||
|
||||
public override AccKind Kind => AccKind.Ratio;
|
||||
|
||||
public override ulong SampleCount => Denominator;
|
||||
|
||||
/// <summary>百分比(0–100);没有样本时为 null。</summary>
|
||||
public double? Percent => Denominator == 0 ? null : 100.0 * Numerator / Denominator;
|
||||
|
||||
public override void Add(double a, double b = 0)
|
||||
{
|
||||
Numerator += (ulong)Math.Max(0, a);
|
||||
Denominator += (ulong)Math.Max(0, b);
|
||||
}
|
||||
|
||||
public override void Merge(Accumulator other)
|
||||
{
|
||||
var o = Cast<RatioAcc>(other);
|
||||
Numerator += o.Numerator;
|
||||
Denominator += o.Denominator;
|
||||
}
|
||||
|
||||
public override Accumulator Clone() => new RatioAcc { Numerator = Numerator, Denominator = Denominator };
|
||||
}
|
||||
|
||||
public sealed class SumAcc : Accumulator
|
||||
{
|
||||
public double Sum { get; set; }
|
||||
|
||||
public ulong Count { get; set; }
|
||||
|
||||
public override AccKind Kind => AccKind.Sum;
|
||||
|
||||
public override ulong SampleCount => Count;
|
||||
|
||||
public double? Mean => Count == 0 ? null : Sum / Count;
|
||||
|
||||
public override void Add(double a, double b = 0)
|
||||
{
|
||||
Sum += a;
|
||||
Count++;
|
||||
}
|
||||
|
||||
public override void Merge(Accumulator other)
|
||||
{
|
||||
var o = Cast<SumAcc>(other);
|
||||
Sum += o.Sum;
|
||||
Count += o.Count;
|
||||
}
|
||||
|
||||
public override Accumulator Clone() => new SumAcc { Sum = Sum, Count = Count };
|
||||
}
|
||||
|
||||
public sealed class MinAcc : Accumulator
|
||||
{
|
||||
public double Value { get; set; } = double.PositiveInfinity;
|
||||
|
||||
public ulong Count { get; set; }
|
||||
|
||||
public override AccKind Kind => AccKind.Min;
|
||||
|
||||
public override ulong SampleCount => Count;
|
||||
|
||||
public double? Result => Count == 0 ? null : Value;
|
||||
|
||||
public override void Add(double a, double b = 0)
|
||||
{
|
||||
Value = Math.Min(Value, a);
|
||||
Count++;
|
||||
}
|
||||
|
||||
public override void Merge(Accumulator other)
|
||||
{
|
||||
var o = Cast<MinAcc>(other);
|
||||
Value = Math.Min(Value, o.Value);
|
||||
Count += o.Count;
|
||||
}
|
||||
|
||||
public override Accumulator Clone() => new MinAcc { Value = Value, Count = Count };
|
||||
}
|
||||
|
||||
public sealed class MaxAcc : Accumulator
|
||||
{
|
||||
public double Value { get; set; } = double.NegativeInfinity;
|
||||
|
||||
public ulong Count { get; set; }
|
||||
|
||||
public override AccKind Kind => AccKind.Max;
|
||||
|
||||
public override ulong SampleCount => Count;
|
||||
|
||||
public double? Result => Count == 0 ? null : Value;
|
||||
|
||||
public override void Add(double a, double b = 0)
|
||||
{
|
||||
Value = Math.Max(Value, a);
|
||||
Count++;
|
||||
}
|
||||
|
||||
public override void Merge(Accumulator other)
|
||||
{
|
||||
var o = Cast<MaxAcc>(other);
|
||||
Value = Math.Max(Value, o.Value);
|
||||
Count += o.Count;
|
||||
}
|
||||
|
||||
public override Accumulator Clone() => new MaxAcc { Value = Value, Count = Count };
|
||||
}
|
||||
|
||||
/// <summary>连续段长度(例如连续丢包):只保留次数、总和和最大值,合并是精确的。</summary>
|
||||
public sealed class RunLengthAcc : Accumulator
|
||||
{
|
||||
public ulong Runs { get; set; }
|
||||
|
||||
public double Total { get; set; }
|
||||
|
||||
public double Max { get; set; }
|
||||
|
||||
public override AccKind Kind => AccKind.RunLength;
|
||||
|
||||
public override ulong SampleCount => Runs;
|
||||
|
||||
public double? Mean => Runs == 0 ? null : Total / Runs;
|
||||
|
||||
public override void Add(double a, double b = 0)
|
||||
{
|
||||
Runs++;
|
||||
Total += a;
|
||||
Max = Math.Max(Max, a);
|
||||
}
|
||||
|
||||
public override void Merge(Accumulator other)
|
||||
{
|
||||
var o = Cast<RunLengthAcc>(other);
|
||||
Runs += o.Runs;
|
||||
Total += o.Total;
|
||||
Max = Math.Max(Max, o.Max);
|
||||
}
|
||||
|
||||
public override Accumulator Clone() => new RunLengthAcc { Runs = Runs, Total = Total, Max = Max };
|
||||
}
|
||||
|
||||
/// <summary>固定分辨率的时间序列(仅用于测试结果):Add(a = 序号, b = 值),合并为拼接。</summary>
|
||||
public sealed class SeriesAcc : Accumulator
|
||||
{
|
||||
public List<double> Points { get; } = [];
|
||||
|
||||
public override AccKind Kind => AccKind.Series;
|
||||
|
||||
public override ulong SampleCount => (ulong)Points.Count;
|
||||
|
||||
public override void Add(double a, double b = 0)
|
||||
{
|
||||
int index = (int)a;
|
||||
while (Points.Count <= index)
|
||||
{
|
||||
Points.Add(double.NaN);
|
||||
}
|
||||
|
||||
Points[index] = b;
|
||||
}
|
||||
|
||||
public override void Merge(Accumulator other) => Points.AddRange(Cast<SeriesAcc>(other).Points);
|
||||
|
||||
public override Accumulator Clone()
|
||||
{
|
||||
var copy = new SeriesAcc();
|
||||
copy.Points.AddRange(Points);
|
||||
return copy;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user