Files
fscan/core/adaptive_timeout.go
T
ZacharyZcR 7063027acf 性能优化: 热路径零分配, 自适应池CAS无锁化, 锁外计算
- port_scan: fmt.Sprintf→JoinHostPort+fmtPort零分配地址格式化 (2.5x)
- port_scan: strings.ToLower→containsFold零分配大小写不敏感匹配 (2.5x)
- port_scan: slidingWindowSchedule修复semaphore泄漏bug
- service_probe: readFromConn预分配4KB缓冲区消除扩容
- adaptive_pool: maybeAdjust用atomic CAS代替持锁检查, 99%免锁
- adaptive_timeout: Timeout锁外计算均值/标准差, 只锁缓存更新
- 新增perf_bench_test.go基准测试验证所有优化
2026-05-18 06:11:10 +08:00

111 lines
2.3 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package core
import (
"math"
"sync"
"time"
)
// AdaptiveTimeout 基于 RTT 采样的自适应超时计算器
// 算法:timeout = mean(RTT) + 4 * stddev(RTT)clamp 到 [min, max]
// 冷启动阶段(样本不足)返回用户配置的固定超时
type AdaptiveTimeout struct {
mu sync.Mutex
samples []float64 // 环形缓冲区,单位 ms
pos int // 写入位置
count int // 已采集总数
size int // 缓冲区容量
minTO time.Duration
maxTO time.Duration
warmup int // 冷启动所需最小样本数
cachedTO time.Duration
dirty bool
}
// NewAdaptiveTimeout 创建自适应超时计算器
// maxTimeout: 用户配置的超时上限(即原始固定超时)
func NewAdaptiveTimeout(maxTimeout time.Duration) *AdaptiveTimeout {
return &AdaptiveTimeout{
samples: make([]float64, 64),
size: 64,
minTO: 100 * time.Millisecond,
maxTO: maxTimeout,
warmup: 10,
}
}
// Record 记录一次成功连接的 RTT
func (a *AdaptiveTimeout) Record(rtt time.Duration) {
a.mu.Lock()
a.samples[a.pos%a.size] = float64(rtt.Milliseconds())
a.pos++
a.count++
a.dirty = true
a.mu.Unlock()
}
// Timeout 获取当前推荐超时值
// 样本不足时返回 maxTO(冷启动)
// 锁外执行均值/标准差计算,减少锁持有时间
func (a *AdaptiveTimeout) Timeout() time.Duration {
a.mu.Lock()
if a.count < a.warmup {
a.mu.Unlock()
return a.maxTO
}
if !a.dirty {
cached := a.cachedTO
a.mu.Unlock()
return cached
}
n := a.size
if a.count < a.size {
n = a.count
}
// 拷贝样本到本地,释放锁后再计算
localSamples := make([]float64, n)
start := a.pos % a.size
if a.count < a.size {
copy(localSamples, a.samples[:n])
} else {
copy(localSamples[:a.size-start], a.samples[start:])
copy(localSamples[a.size-start:], a.samples[:start])
}
a.mu.Unlock()
// 锁外计算
var sum float64
for _, s := range localSamples {
sum += s
}
mean := sum / float64(n)
var variance float64
for _, s := range localSamples {
d := s - mean
variance += d * d
}
stddev := math.Sqrt(variance / float64(n))
ms := mean + 4*stddev
to := time.Duration(ms) * time.Millisecond
if to < a.minTO {
to = a.minTO
}
if to > a.maxTO {
to = a.maxTO
}
// 短暂加锁更新缓存
a.mu.Lock()
a.cachedTO = to
a.dirty = false
a.mu.Unlock()
return to
}