measurement: adapt the echo probe rate to loss and RTT, and weight samples by the time they stand for

udp_echo and tcp_echo run at the lowest rate that still proves the loss bound and keeps the RTT p99 confidence interval within tolerance, go to the maximum when the line loses packets or its RTT rises, and back off exponentially when it is unreachable. Every echo row carries a weight (the time it stands for, in grid intervals) and the analysis ops accumulate loss and RTT by it, so p50/p95/p99 and loss stay time-averaged when the rate changes. State changes are logged for audit.
This commit is contained in:
justme committed 2026-10-22 05:28:28 +08:00
1 parent 57acecdb86
commit e0376b5e33
15 files changed
+858 -55

No files matched your search

@@ -8,10 +8,14 @@ public static class Kinds
{
private static readonly string[] s_directions = ["fwd", "rev", "bidir"];
/// <summary>这个任务可能达到的最高探测频率(Hz):自适应时是 <c>rate_max_hz</c>,否则是固定的 <c>rate_hz</c>。预算、限速和准入都按它算。</summary>
public static long PeakRateHz(string kindId, ParamValues values) =>
(kindId is "udp_echo" or "tcp_echo") && values.GetBool("adaptive") ? Math.Max(values.GetInt("rate_max_hz"), values.GetInt("rate_min_hz")) : values.GetInt("rate_hz");
private static readonly StreamSpec s_echoMsg = new(
"msg",
[
Col.Seq("seq"), Col.U16("size"),
Col.Seq("seq"), Col.U16("size"), Col.U16("w"),
Col.TsDialer("t1"), Col.TsListener("t2", nullable: true), Col.TsListener("t3", nullable: true), Col.TsDialer("t4", nullable: true),
],
Side.Dialer,
@@ -33,12 +37,17 @@ public static class Kinds
Scope.Line,
new BlockSet("quic_datagram", "fixed", "fixed", "echo", []),
[
P.Int("rate_hz", 10, 1, 200, "Hz"),
P.Bool("adaptive", true, "Adapt the probe rate to the loss rate: low when the line is quiet, up to rate_max_hz when it loses packets, exponential back-off when it is unreachable."),
P.Int("rate_hz", 10, 1, 200, "Hz", "Fixed rate, used when adaptive is false."),
P.Int("rate_min_hz", 2, 1, 200, "Hz"),
P.Int("rate_max_hz", 10, 1, 200, "Hz", "Highest rate, and the reference grid for sample weights."),
P.Number("tau_pct", 0.2, 0.01, 5, "%", "Loss rate a quiet line has to be able to prove below, at 95% confidence."),
P.Number("rtt_tol_pct", 2, 0.1, 20, "%", "Half-width of the 95% confidence interval of the RTT p99 a quiet line has to keep, as a share of p99 (never below 0.3 ms)."),
P.Int("size", 64, 44, 1400, "B"),
P.Int("port", 7443, 1, 65535),
],
[s_echoMsg],
p => Bandwidth.UdpEcho(p.GetInt("rate_hz"), p.GetInt("size")));
p => Bandwidth.UdpEcho(PeakRateHz("udp_echo", p), p.GetInt("size")));
public static readonly KindSpec UdpTrain = new(
"udp_train",
@@ -66,15 +75,20 @@ public static class Kinds
Scope.Line,
new BlockSet("tcp", "fixed", "uniform", "echo", ["tcp_info"]),
[
P.Int("rate_hz", 20, 1, 200, "Hz"),
P.Bool("adaptive", true, "Adapt the echo rate: as low as the loss bound and the RTT percentile precision allow when the line is quiet, the maximum when it is not, back-off when it stalls."),
P.Int("rate_hz", 20, 1, 200, "Hz", "Fixed rate, used when adaptive is false."),
P.Int("rate_min_hz", 2, 1, 200, "Hz"),
P.Int("rate_max_hz", 20, 1, 200, "Hz", "Highest rate, and the reference grid for sample weights."),
P.Number("tau_pct", 0.2, 0.01, 5, "%", "Share of retransmitted segments a quiet line has to be able to prove below, at 95% confidence."),
P.Number("rtt_tol_pct", 2, 0.1, 20, "%", "Half-width of the 95% confidence interval of the RTT p99 a quiet line has to keep, as a share of p99 (never below 0.3 ms)."),
P.Int("min", 32, 16, 1400, "B"),
P.Int("max", 64, 16, 1400, "B"),
P.Int("tcp_info_ms", 100, 10, 1000, "ms"),
P.Int("tcp_info_ms", 1000, 10, 1000, "ms"),
P.Int("port", 7444, 1, 65535),
P.Bool("tls", false),
],
[s_echoMsg, s_tcpInfo],
p => Bandwidth.TcpEcho(p.GetInt("rate_hz"), p.GetInt("max")));
p => Bandwidth.TcpEcho(PeakRateHz("tcp_echo", p), p.GetInt("max")));
public static readonly KindSpec TcpBulk = new(
"tcp_bulk",