throughput: the upstream direction, counted by the only party that can
The client generates the traffic and the server counts it. No grant is involved - the client is sending its own packets, so there is nothing to amplify - but it does need the server's tally, because only the far end knows how much arrived. Without that number a sender measures how fast it can transmit, which is usually just the speed of the local NIC and is not the question being asked. A new wire type the server counts and deliberately never answers: a reply would double the traffic and drag the return path into a measurement that is specifically about the outbound one. The tally is a counter, not a list, and short-circuits before the observation log. A five-second run at 20 Mbps is around ten thousand packets; one struct each would turn a measurement into an allocation storm on a shared server, and nothing needs the per-packet detail since the client holds the send-side record. The gap between the two counts is the loss. direction=up on the throughput action sends nothing - it zeroes the counter, so a second run in one session measures itself instead of inheriting the first. Same honesty rule as downstream: measures_network is false when what arrived matches what was offered, because then the path was never the constraint. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
8646bab52d
commit
892e952a8e
@@ -39,6 +39,10 @@ const (
|
||||
TypeFragData = 0x0D
|
||||
// TypeThroughputData is one packet of a sustained-rate downstream run.
|
||||
TypeThroughputData = 0x0E
|
||||
// TypeThroughputUp is one packet of a client-driven upstream run. The server counts it and
|
||||
// deliberately does not answer: a reply would double the traffic and measure the return
|
||||
// path at the same time, which is the one thing this test is trying not to do.
|
||||
TypeThroughputUp = 0x0F
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
@@ -152,6 +156,14 @@ func (s *Server) handle(conn *net.UDPConn, raddr netip.AddrPort, pkt []byte, tRx
|
||||
if la, ok := conn.LocalAddr().(*net.UDPAddr); ok {
|
||||
sess.NoteDataLocal(la.AddrPort())
|
||||
}
|
||||
|
||||
// Upstream throughput short-circuits before the observation log. Recording one struct per
|
||||
// packet here would mean tens of thousands of allocations for a single run; the counter is
|
||||
// all anyone needs, since the client holds the send-side record.
|
||||
if typ == TypeThroughputUp {
|
||||
sess.CountUpstream(len(pkt), tRxNs)
|
||||
return
|
||||
}
|
||||
sess.RecordUDP(session.UDPObservation{
|
||||
Seq: seq, TRxNs: tRxNs, TTxNs: time.Since(s.start).Nanoseconds(),
|
||||
Src: raddr.String(), Size: len(pkt), Type: typ,
|
||||
|
||||
Reference in New Issue
Block a user