package speedstats import ( "math/rand/v2" "sync" "time" jcstypes "gitlink.org.cn/cloudream/jcs-pub/common/types" ) const ( LastStatsWeigth = 0.8 MinProb = 0.05 ) type Stats struct { TotalSize int64 TotalTime time.Duration LastSpeed float64 HasLastSpeed bool AvarageSpeed float64 } func (s *Stats) Record(size int64, time time.Duration) { s.TotalSize += size s.TotalTime += time if s.HasLastSpeed { s.AvarageSpeed = float64(s.TotalSize)*(1-LastStatsWeigth)/s.TotalTime.Seconds() + s.LastSpeed*LastStatsWeigth } else { s.AvarageSpeed = float64(s.TotalSize) / s.TotalTime.Seconds() } } func (s *Stats) Step() { s.TotalSize = 0 s.TotalTime = 0 s.LastSpeed = s.AvarageSpeed s.AvarageSpeed = s.LastSpeed * LastStatsWeigth s.HasLastSpeed = true } type SpeedStats struct { lock sync.RWMutex stats100M []Stats stats1G []Stats statsAbove1G []Stats } func New() *SpeedStats { return &SpeedStats{ stats100M: make([]Stats, 2), stats1G: make([]Stats, 2), statsAbove1G: make([]Stats, 2), } } func (p *SpeedStats) Record(size int64, elapsedTime time.Duration, happenedAtClient bool) { p.lock.Lock() defer p.lock.Unlock() if size < 100*1024*1024 { if happenedAtClient { p.stats100M[0].Record(size, elapsedTime) } else { p.stats100M[1].Record(size, elapsedTime) } } else if size < 1024*1024*1024 { if happenedAtClient { p.stats1G[0].Record(size, elapsedTime) } else { p.stats1G[1].Record(size, elapsedTime) } } else { if happenedAtClient { p.statsAbove1G[0].Record(size, elapsedTime) } else { p.statsAbove1G[1].Record(size, elapsedTime) } } } func (p *SpeedStats) Step() { p.lock.Lock() defer p.lock.Unlock() p.stats100M[0].Step() p.stats100M[1].Step() p.stats1G[0].Step() p.stats1G[1].Step() p.statsAbove1G[0].Step() p.statsAbove1G[1].Step() } func (p *SpeedStats) ShouldAtClient(size int64) bool { p.lock.RLock() defer p.lock.RUnlock() var ss []Stats if size < 100*1024*1024 { ss = p.stats100M } else if size < 1024*1024*1024 { ss = p.stats1G } else { ss = p.statsAbove1G } prob := 0.0 if ss[0].AvarageSpeed == 0 || ss[1].AvarageSpeed == 0 { prob = 0.5 } else { // 保证最小概率为0.05,最大概率为0.95 totalSpeed := ss[0].AvarageSpeed + ss[1].AvarageSpeed scale := 1 - 2*MinProb prob = ss[0].AvarageSpeed/totalSpeed*scale + MinProb } v := rand.Float64() return v < prob } func (p *SpeedStats) DumpStatus() jcstypes.SpeedStatsStatus { return jcstypes.SpeedStatsStatus{ Below100M: []jcstypes.SpeedStatsStatusEntry{ { TotalSize: p.stats100M[0].TotalSize, TotalTime: p.stats100M[0].TotalTime, AvarageSpeed: p.stats100M[0].AvarageSpeed, LastSpeed: p.stats100M[0].LastSpeed, }, { TotalSize: p.stats100M[1].TotalSize, TotalTime: p.stats100M[1].TotalTime, AvarageSpeed: p.stats100M[1].AvarageSpeed, LastSpeed: p.stats100M[1].LastSpeed, }, }, Below1G: []jcstypes.SpeedStatsStatusEntry{ { TotalSize: p.stats1G[0].TotalSize, TotalTime: p.stats1G[0].TotalTime, AvarageSpeed: p.stats1G[0].AvarageSpeed, LastSpeed: p.stats1G[0].LastSpeed, }, { TotalSize: p.stats1G[1].TotalSize, TotalTime: p.stats1G[1].TotalTime, AvarageSpeed: p.stats1G[1].AvarageSpeed, LastSpeed: p.stats1G[1].LastSpeed, }, }, Above1G: []jcstypes.SpeedStatsStatusEntry{ { TotalSize: p.statsAbove1G[0].TotalSize, TotalTime: p.statsAbove1G[0].TotalTime, AvarageSpeed: p.statsAbove1G[0].AvarageSpeed, LastSpeed: p.statsAbove1G[0].LastSpeed, }, { TotalSize: p.statsAbove1G[1].TotalSize, TotalTime: p.statsAbove1G[1].TotalTime, AvarageSpeed: p.statsAbove1G[1].AvarageSpeed, LastSpeed: p.statsAbove1G[1].LastSpeed, }, }, } }