引入null!,解决一些几乎肯定不为空的成员被Null分析提示的问题
大石头 authored at 2023-09-18 11:27:20
4.18 KiB
X
using System.Diagnostics;
using NewLife.Threading;

namespace NewLife.Log;

/// <summary>性能计数器。次数、TPS、平均耗时</summary>
public class PerfCounter : DisposeBase, ICounter
{
    #region 属性
    /// <summary>是否启用。默认true</summary>
    public Boolean Enable { get; set; } = true;

    private Int64 _Value;
    /// <summary>数值</summary>
    public Int64 Value => _Value;

    private Int64 _Times;
    /// <summary>次数</summary>
    public Int64 Times => _Times;

    /// <summary>耗时,单位us</summary>
    private Int64 _TotalCost;
    #endregion

    #region 构造
    /// <summary>销毁</summary>
    /// <param name="disposing"></param>
    protected override void Dispose(Boolean disposing)
    {
        base.Dispose(disposing);

        _Timer.TryDispose();
    }
    #endregion

    #region 核心方法
    /// <summary>增加</summary>
    /// <param name="value">增加的数量</param>
    /// <param name="usCost">耗时,单位us</param>
    public void Increment(Int64 value, Int64 usCost)
    {
        if (!Enable) return;

        // 累加总次数和总数值
        Interlocked.Add(ref _Value, value);
        Interlocked.Increment(ref _Times);
        if (usCost > 0) Interlocked.Add(ref _TotalCost, usCost);

        if (_Timer == null)
        {
            lock (this)
            {
                _Timer ??= new TimerX(DoWork, null, Interval, Interval) { Async = true };
            }
        }
    }
    #endregion

    #region 采样
    /// <summary>采样间隔,默认1000毫秒</summary>
    public Int32 Interval { get; set; } = 1000;

    /// <summary>持续采样时间,默认60秒</summary>
    public Int32 Duration { get; set; } = 60;

    /// <summary>当前速度</summary>
    public Int64 Speed { get; private set; }

    /// <summary>最大速度</summary>
    public Int64 MaxSpeed => _quSpeed.Max();

    /// <summary>最后一个采样周期的平均耗时,单位us</summary>
    public Int64 Cost { get; private set; }

    /// <summary>持续采样时间内的最大平均耗时,单位us</summary>
    public Int64 MaxCost => _quCost.Max();

    private Int64[] _quSpeed = new Int64[60];
    private Int64[] _quCost = new Int64[60];
    private Int32 _queueIndex = -1;

    private TimerX? _Timer;
    private Stopwatch? _sw;
    private Int64 _LastValue;
    private Int64 _LastTimes;
    private Int64 _LastCost;

    /// <summary>定期采样,保存最近60组到数组队列里面</summary>
    /// <param name="state"></param>
    private void DoWork(Object? state)
    {
        // 计算采样次数
        var len = Duration * 1000 / Interval;

        if (_quSpeed.Length != len) _quSpeed = new Int64[len];
        if (_quCost.Length != len) _quCost = new Int64[len];

        // 计算速度
        var sp = 0L;
        var cc = 0L;
        if (_sw == null)
            _sw = Stopwatch.StartNew();
        else
        {
            var ms = _sw.Elapsed.TotalMilliseconds;
            _sw.Restart();

            sp = (Int64)((Value - _LastValue) * 1000 / ms);
        }

        _LastValue = Value;

        // 计算本周期平均耗时
        // 本周期内的总耗时除以总次数,得到本周期平均耗时
        var ts = Times - _LastTimes;
        cc = ts == 0 ? Cost : ((_TotalCost - _LastCost) / ts);

        _LastTimes = Times;
        _LastCost = _TotalCost;

        Speed = sp;
        Cost = cc;

        // 进入队列
        _queueIndex++;
        if (_queueIndex < 0 || _queueIndex >= len) _queueIndex = 0;
        _quSpeed[_queueIndex] = sp;
        _quCost[_queueIndex] = cc;
    }
    #endregion

    #region 辅助
    /// <summary>已重载。输出统计信息</summary>
    /// <returns></returns>
    public override String ToString()
    {
        if (Cost >= 1000)
            return $"{Times:n0}/{MaxSpeed:n0}/{Speed:n0}tps/{MaxCost / 1000:n0}/{Cost / 1000:n0}ms";
        if (Cost > 0)
            return $"{Times:n0}/{MaxSpeed:n0}/{Speed:n0}tps/{MaxCost:n0}/{Cost:n0}us";
        else
            return $"{Times:n0}/{MaxSpeed:n0}/{Speed:n0}tps";
    }
    #endregion
}