From 5ecd3cfe4dd73e7f39f21750df18dcdb754ab063 Mon Sep 17 00:00:00 2001 From: ZacharyZcR Date: Wed, 21 Jan 2026 18:35:44 +0800 Subject: [PATCH] =?UTF-8?q?perf(icmp):=20=E5=AE=9E=E7=8E=B0=E8=87=AA?= =?UTF-8?q?=E9=80=82=E5=BA=94=E7=AD=89=E5=BE=85=E7=AE=97=E6=B3=95=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E5=AD=98=E6=B4=BB=E6=A3=80=E6=B5=8B=E6=80=A7=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 waitAdaptive 函数,监控响应增量实现智能提前结束 - 算法保守原则:最小等待1s + 连续500ms无新响应才提前结束 - 添加100ms检查间隔避免CPU空转 - 保留原有最大等待时间(3s/6s)作为兜底 - 添加完整单元测试覆盖各种场景 优化效果: - 全部响应:~100ms (原3s) - 无响应:~1s (原3s) - 部分响应后稳定:~1.5s (原3s) --- core/icmp.go | 99 ++++++++++++++++++++++++++++++---------- core/icmp_test.go | 113 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 188 insertions(+), 24 deletions(-) diff --git a/core/icmp.go b/core/icmp.go index 6f74590..1fedcba 100644 --- a/core/icmp.go +++ b/core/icmp.go @@ -202,6 +202,77 @@ func printAliveStats(aliveHosts []string, hostslist []string) { } } +// ICMP 自适应等待参数 +const ( + icmpCheckInterval = 100 * time.Millisecond // 检查间隔,避免 CPU 空转 + icmpMinWaitTime = 1 * time.Second // 最小等待时间,确保基础响应收集 + icmpStableThreshold = 500 * time.Millisecond // 无新响应稳定阈值,超过此时间无新响应则提前结束 +) + +// waitAdaptive 自适应等待 ICMP 响应 +// 算法:监控响应增量,连续一段时间无新响应则提前结束 +// 保守原则: +// - 必须等待最小时间 (1s),确保基础响应收集 +// - 只有"连续 500ms 无新响应"才提前结束 +// - 保留原有最大等待时间作为兜底 +func waitAdaptive(hostslist []string, aliveHosts *[]string, aliveHostsMu *sync.Mutex) { + totalHosts := len(hostslist) + + // 根据主机数量设置最大超时时间(保持原有逻辑作为兜底) + maxWait := 6 * time.Second + if totalHosts <= 256 { + maxWait = 3 * time.Second + } + + start := time.Now() + lastAliveCount := 0 + lastChangeTime := start + + for { + time.Sleep(icmpCheckInterval) // 避免 CPU 空转 + + // 读取当前存活数 + aliveHostsMu.Lock() + aliveCount := len(*aliveHosts) + aliveHostsMu.Unlock() + + elapsed := time.Since(start) + + // 条件1:所有主机都已响应,立即结束 + if aliveCount >= totalHosts { + common.LogDebug(fmt.Sprintf("[ICMP] 全部响应,耗时 %v", elapsed.Round(time.Millisecond))) + break + } + + // 条件2:超过最大等待时间,兜底结束 + if elapsed >= maxWait { + common.LogDebug(fmt.Sprintf("[ICMP] 达到最大等待时间 %v,存活 %d/%d", maxWait, aliveCount, totalHosts)) + break + } + + // 条件3:自适应提前结束 + // 必须满足:已过最小等待时间 + 连续一段时间没有新响应 + if elapsed >= icmpMinWaitTime { + if aliveCount > lastAliveCount { + // 有新响应,更新状态 + lastChangeTime = time.Now() + lastAliveCount = aliveCount + } else if time.Since(lastChangeTime) >= icmpStableThreshold { + // 连续 500ms 没有新响应,认为响应已稳定,提前结束 + common.LogDebug(fmt.Sprintf("[ICMP] 响应稳定,提前结束,耗时 %v,存活 %d/%d", + elapsed.Round(time.Millisecond), aliveCount, totalHosts)) + break + } + } else { + // 最小等待期内,持续更新状态 + if aliveCount > lastAliveCount { + lastChangeTime = time.Now() + lastAliveCount = aliveCount + } + } + } +} + // RunIcmp1 使用ICMP批量探测主机存活(监听模式) func RunIcmp1(hostslist []string, conn *icmp.PacketConn, chanHosts chan string, aliveHosts *[]string, aliveHostsMu *sync.Mutex, config *common.Config, state *common.State, livewg *sync.WaitGroup) { // 使用atomic.Bool保证并发安全 @@ -272,30 +343,10 @@ func RunIcmp1(hostslist []string, conn *icmp.PacketConn, chanHosts chan string, _, _ = conn.WriteTo(IcmpByte, dst) } - // 等待响应 - start := time.Now() - for { - // 加锁读取aliveHosts长度 - aliveHostsMu.Lock() - aliveCount := len(*aliveHosts) - aliveHostsMu.Unlock() - - // 所有主机都已响应则退出 - if aliveCount == len(hostslist) { - break - } - - // 根据主机数量设置超时时间 - since := time.Since(start) - wait := time.Second * 6 - if len(hostslist) <= 256 { - wait = time.Second * 3 - } - - if since > wait { - break - } - } + // 自适应等待响应 + // 算法:监控响应增量,连续一段时间无新响应则提前结束 + // 保守原则:保留最大等待时间兜底,确保不漏掉慢响应主机 + waitAdaptive(hostslist, aliveHosts, aliveHostsMu) endflag.Store(true) _ = conn.Close() diff --git a/core/icmp_test.go b/core/icmp_test.go index 289fa89..6c8f182 100644 --- a/core/icmp_test.go +++ b/core/icmp_test.go @@ -2,7 +2,9 @@ package core import ( "fmt" + "sync" "testing" + "time" ) // TestCheckSum 测试ICMP校验和计算(RFC 1071算法) @@ -484,6 +486,117 @@ func TestMakemsg(t *testing.T) { }) } +// TestWaitAdaptive 测试自适应等待算法 +func TestWaitAdaptive(t *testing.T) { + t.Run("全部响应-立即结束", func(t *testing.T) { + hostslist := []string{"192.168.1.1", "192.168.1.2", "192.168.1.3"} + aliveHosts := []string{"192.168.1.1", "192.168.1.2", "192.168.1.3"} // 全部存活 + var mu sync.Mutex + + start := time.Now() + waitAdaptive(hostslist, &aliveHosts, &mu) + elapsed := time.Since(start) + + // 全部响应应该在 1 个检查周期内结束 (~100ms) + if elapsed > 200*time.Millisecond { + t.Errorf("全部响应后应快速结束,实际耗时 %v", elapsed) + } + }) + + t.Run("无响应-自适应提前结束", func(t *testing.T) { + hostslist := make([]string, 10) // 10 个主机 + for i := range hostslist { + hostslist[i] = fmt.Sprintf("192.168.1.%d", i+1) + } + aliveHosts := []string{} // 无响应 + var mu sync.Mutex + + start := time.Now() + waitAdaptive(hostslist, &aliveHosts, &mu) + elapsed := time.Since(start) + + // 无响应时:lastChangeTime = start + // 在 minWait(1s) 后,time.Since(lastChangeTime) >= 1s > stableThreshold(500ms) + // 所以会在约 1s 时提前结束(这是自适应优化的效果) + // 相比原来的固定 3s,节省了约 2s + if elapsed < 900*time.Millisecond || elapsed > 1300*time.Millisecond { + t.Errorf("无响应时应在约 1s 提前结束,实际耗时 %v", elapsed) + } + }) + + t.Run("部分响应后稳定-提前结束", func(t *testing.T) { + hostslist := make([]string, 100) + for i := range hostslist { + hostslist[i] = fmt.Sprintf("192.168.1.%d", i+1) + } + // 模拟 50% 响应 + aliveHosts := make([]string, 50) + for i := range aliveHosts { + aliveHosts[i] = fmt.Sprintf("192.168.1.%d", i+1) + } + var mu sync.Mutex + + start := time.Now() + waitAdaptive(hostslist, &aliveHosts, &mu) + elapsed := time.Since(start) + + // 响应已稳定(不再变化),应该在 minWait + stableThreshold 后结束 + // 即约 1.5s,而不是 3s + if elapsed > 2*time.Second { + t.Errorf("响应稳定后应提前结束,实际耗时 %v", elapsed) + } + }) + + t.Run("持续响应-等待完成", func(t *testing.T) { + hostslist := make([]string, 10) + for i := range hostslist { + hostslist[i] = fmt.Sprintf("192.168.1.%d", i+1) + } + aliveHosts := []string{} + var mu sync.Mutex + + // 模拟持续响应:每 200ms 增加一个存活主机 + done := make(chan struct{}) + go func() { + defer close(done) + for i := 0; i < 10; i++ { + time.Sleep(200 * time.Millisecond) + mu.Lock() + aliveHosts = append(aliveHosts, fmt.Sprintf("192.168.1.%d", i+1)) + mu.Unlock() + } + }() + + start := time.Now() + waitAdaptive(hostslist, &aliveHosts, &mu) + elapsed := time.Since(start) + <-done // 等待 goroutine 结束 + + // 10 个主机 * 200ms = 2s,全部响应后应立即结束 + // 总耗时应该在 2s 左右 + if elapsed < 1800*time.Millisecond || elapsed > 2500*time.Millisecond { + t.Errorf("持续响应时应等待全部完成,实际耗时 %v", elapsed) + } + }) +} + +// BenchmarkWaitAdaptive 基准测试自适应等待性能 +func BenchmarkWaitAdaptive(b *testing.B) { + hostslist := make([]string, 100) + for i := range hostslist { + hostslist[i] = fmt.Sprintf("192.168.1.%d", i+1) + } + // 全部响应场景 + aliveHosts := make([]string, 100) + copy(aliveHosts, hostslist) + var mu sync.Mutex + + b.ResetTimer() + for i := 0; i < b.N; i++ { + waitAdaptive(hostslist, &aliveHosts, &mu) + } +} + // BenchmarkCheckSum 基准测试校验和性能 func BenchmarkCheckSum(b *testing.B) { msg := make([]byte, 40)