Repository navigation
v0.4.0
The measurement algorithm reaches a confident answer on links the previous
release could not finish measuring, and every figure now says which part of
the run it came from. schema_version stays 1: all result fields are additions.
Added
- A throughput lower bound. Each direction reports
throughput_lower_bound_bps— the lowest goodput of the latest four
consecutive measured intervals that agree within tolerance — with
lower_bound_windowandrpm_upper_bound(the responsiveness over that
window). It is present whenever such a window formed, converged or not, so
a caller that gets a medium-confidence estimate still has a figure that
holds. The CLI prints it after the estimate; interval events carryhold
(#20). loaded_windowreports the intervalsrpmandloadedwere computed
over, andnqserver --idle-timeout(default 2 min) bounds how long a
connection with no request in flight is kept (#24).
Changed
- Ramp-and-hold algorithm (#20). Flows are added in doubling steps and
the ramp stops when a step gains less than 10 % goodput; responsiveness is
tracked from the end of the ramp and judged on its windowed values; a
goodput drop of more than 25 % restarts goodput tracking; upload intervals
inflated by the HTTP/2 send window of new flows are excluded. Every
simulated scenario now converges with high confidence inside the 12 s
budget — 10 Gbps at 150 ms RTT, CDN per-flow caps, shaper bursts, a
capacity change mid-run — links ≤ 100 Mbps finish 1–3 s sooner, and a
20 Mbps upload reports 20 Mbps instead of 53. NewStabilityParams
fields:SendBufferBytes,RampGainTolerance,ChangeTolerance;
DefaultUploadSendBuffer.FlowIncrementis now the floor of a step. rpmandloaded.*are computed over every probe sample since throughput
became stable, never fewer than the draft's trailing four intervals, rather
than over the trailing intervals alone. Foreign probes are sparse — a TLS
handshake each — so the old window could hold self samples and no foreign
one, andloaded.foreignwas then omitted as if no fresh-connection probe
had ever succeeded. A series with samples in the phase but none in the
window is now named in a warning (#24).
Fixed
- Windows latency figures are no longer quantised to the system timer
tick. Go'stime.Nowtakes its monotonic reading from_INTERRUPT_TIME,
which advances only every 0.5–15.625 ms depending on what else is running
on the machine, so probe durations — tens of microseconds on a fast path —
measured either 0 or a whole tick. Means survived that, which is whyrpm
looked plausible, but jitter was overstated by roughly 7×, the median
snapped to a tick boundary andp95was tens of percent high; a probe
shorter than a tick could collapseself_rpmto 0 entirely. Probe timings
now readQueryPerformanceCounter, as Go's owntestingpackage does for
benchmarks (LAT-10). Other platforms are unchanged. If the high-resolution
counter cannot be reached the run says so inwarningsrather than
presenting coarse numbers as exact. - Throughput no longer measures the test's own probe traffic. The fixed
per-probe byte estimate (5000 B foreign, 1000 B self) was added to the same
counter the engine reads as goodput, so a stalled or very slow path
reported hundreds of kbps that were purely probes — and, because probe
spacing is derived from that estimate, the client throttled itself to about
one probe per second on exactly the paths that most need probing. Probe
cost still counts againstMaxBytesandbytes(LIM-2);throughput_bps,
peak_throughput_bpsandmean_throughput_bpsnow count load-flow bytes
only (LOAD-4, LOAD-11). nqservercloses idle connections and pings silent HTTP/2 peers (30 s +
15 s); previously a connection whose client had gone quiet was held until
the process exited.server.LimitListener:Closenow releases an
Acceptwaiting at the connection cap, sohttp.Server.Servereturns and
Shutdownno longer waits out its timeout (#24).- Data race in the load phase: the reason a phase stopped was read on the
phase goroutine before the flow and probe goroutines were joined, while any
of them could still be writing it.