Skip to content

Commit c3544e4

Browse files
Add summary at the end of the measurement and fix RTT calc (#45)
* Add rtt/minrtt * Add summary at the end of the measurement * Fix output
1 parent 068bb2b commit c3544e4

File tree

3 files changed

+96
-36
lines changed

3 files changed

+96
-36
lines changed

cmd/msak-client/client.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,4 +65,6 @@ func main() {
6565
if *flagUpload {
6666
cl.Upload(context.Background())
6767
}
68+
69+
cl.PrintSummary()
6870
}

pkg/client/client.go

Lines changed: 75 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ const (
3030
DefaultWebSocketHandshakeTimeout = 5 * time.Second
3131

3232
// DefaultStreams is the default number of streams for a new client.
33-
DefaultStreams = 3
33+
DefaultStreams = 2
3434

3535
// DefaultLength is the default test duration for a new client.
3636
DefaultLength = 5 * time.Second
@@ -92,10 +92,23 @@ type Throughput1Client struct {
9292
// It is set when the first streams connects to the server and used to compute the elapsed time.
9393
sharedStartTime time.Time
9494
started atomic.Bool
95+
96+
// rtt is the latest RTT value from TCPInfo.
97+
rtt atomic.Uint32
98+
99+
// minRTT is the lowest RTT value observed across all streams.
100+
minRTT atomic.Uint32
101+
102+
// lastResultForSubtest contains the last recorded measurement for the
103+
// corresponding subtest (download/upload).
104+
lastResultForSubtest map[spec.SubtestKind]Result
105+
lastResultForSubtestMutex sync.Mutex
95106
}
96107

97108
// Result contains the aggregate metrics collected during the test.
98109
type Result struct {
110+
// Subtest is the subtest this Result refers to.
111+
Subtest spec.SubtestKind
99112
// Goodput is the average number of application-level bits per second that
100113
// have been transferred so far across all the streams.
101114
Goodput float64
@@ -104,8 +117,18 @@ type Result struct {
104117
Throughput float64
105118
// Elapsed is the total time elapsed since the test started.
106119
Elapsed time.Duration
107-
// MinRTT is the minimum of MinRTT values observed across all the streams.
120+
// RTT is the latest RTT value from TCPInfo from any stream.
121+
RTT uint32
122+
// MinRTT is the minimum of RTT values observed across all the streams.
108123
MinRTT uint32
124+
// Streams is the number of streams used in the test.
125+
Streams int
126+
// ByteLimit is the byte limit used in the test.
127+
ByteLimit int
128+
// Length is the length of the test.
129+
Length time.Duration
130+
// CongestionControl is the congestion control used in the test.
131+
CongestionControl string
109132
}
110133

111134
// makeUserAgent creates the user agent string.
@@ -131,6 +154,8 @@ func New(clientName, clientVersion string, config Config) *Throughput1Client {
131154

132155
tIndex: map[string]int{},
133156
recvByteCounters: map[int][]int64{},
157+
158+
lastResultForSubtest: map[spec.SubtestKind]Result{},
134159
}
135160
}
136161

@@ -214,6 +239,7 @@ func (c *Throughput1Client) start(ctx context.Context, subtest spec.SubtestKind)
214239

215240
// Reset the counters.
216241
c.recvByteCounters = map[int][]int64{}
242+
c.rtt.Store(0)
217243

218244
startTimeCh := make(chan time.Time, 1)
219245

@@ -253,7 +279,6 @@ func (c *Throughput1Client) start(ctx context.Context, subtest spec.SubtestKind)
253279
}
254280

255281
wg.Wait()
256-
257282
return nil
258283
}
259284

@@ -303,40 +328,39 @@ func (c *Throughput1Client) runStream(ctx context.Context, streamID int, mURL *u
303328
clientCh, serverCh, errCh = proto.SenderLoop(ctx)
304329
}
305330

331+
var m model.WireMeasurement
332+
306333
for {
307334
select {
308335
case <-ctx.Done():
309-
c.config.Emitter.OnComplete(streamID, mURL.Host)
336+
c.config.Emitter.OnStreamComplete(streamID, mURL.Host)
310337
return nil
311-
case m := <-clientCh:
338+
case m = <-clientCh:
312339
// If subtest is download, store the client-side measurement.
313340
if subtest != spec.SubtestDownload {
314341
continue
315342
}
316-
c.config.Emitter.OnMeasurement(streamID, m)
317-
c.config.Emitter.OnDebug(fmt.Sprintf("Stream #%d - application r/w: %d/%d, network r/w: %d/%d",
318-
streamID, m.Application.BytesReceived, m.Application.BytesSent,
319-
m.Network.BytesReceived, m.Network.BytesSent))
320-
c.storeMeasurement(streamID, m)
321-
if c.started.Load() {
322-
c.emitResult(c.sharedStartTime)
323-
}
324-
case m := <-serverCh:
343+
case m = <-serverCh:
325344
// If subtest is upload, store the server-side measurement.
326345
if subtest != spec.SubtestUpload {
327346
continue
328347
}
329-
c.config.Emitter.OnMeasurement(streamID, m)
330-
c.config.Emitter.OnDebug(fmt.Sprintf("#%d - application r/w: %d/%d, network r/w: %d/%d",
331-
streamID, m.Application.BytesReceived, m.Application.BytesSent,
332-
m.Network.BytesReceived, m.Network.BytesSent))
333-
c.storeMeasurement(streamID, m)
334-
if c.started.Load() {
335-
c.emitResult(c.sharedStartTime)
336-
}
337348
case err := <-errCh:
338349
return err
339350
}
351+
352+
c.config.Emitter.OnMeasurement(streamID, m)
353+
c.config.Emitter.OnDebug(fmt.Sprintf("Stream #%d - application r/w: %d/%d, network r/w: %d/%d",
354+
streamID, m.Application.BytesReceived, m.Application.BytesSent,
355+
m.Network.BytesReceived, m.Network.BytesSent))
356+
c.storeMeasurement(streamID, m)
357+
if c.started.Load() {
358+
res := c.computeResult(subtest)
359+
c.config.Emitter.OnResult(res)
360+
c.lastResultForSubtestMutex.Lock()
361+
c.lastResultForSubtest[subtest] = res
362+
c.lastResultForSubtestMutex.Unlock()
363+
}
340364
}
341365
}
342366

@@ -345,6 +369,16 @@ func (c *Throughput1Client) storeMeasurement(streamID int, m model.WireMeasureme
345369
c.recvByteCountersMutex.Lock()
346370
c.recvByteCounters[streamID] = append(c.recvByteCounters[streamID], m.Application.BytesReceived)
347371
c.recvByteCountersMutex.Unlock()
372+
373+
if m.TCPInfo != nil {
374+
if m.TCPInfo.RTT > 0 {
375+
c.rtt.Store(m.TCPInfo.RTT)
376+
}
377+
minRTT := c.minRTT.Load()
378+
if m.TCPInfo.MinRTT > 0 && (minRTT == 0 || m.TCPInfo.MinRTT < minRTT) {
379+
c.minRTT.Store(m.TCPInfo.MinRTT)
380+
}
381+
}
348382
}
349383

350384
// applicationBytes returns the aggregate application-level bytes transferred by all the streams.
@@ -360,17 +394,23 @@ func (c *Throughput1Client) applicationBytes() int64 {
360394
return sum
361395
}
362396

363-
// emitResult emits the result of the current measurement via the configured Emitter.
364-
func (c *Throughput1Client) emitResult(start time.Time) {
397+
// computeResult returns a Result struct with the current state of the measurement.
398+
func (c *Throughput1Client) computeResult(subtest spec.SubtestKind) Result {
365399
applicationBytes := c.applicationBytes()
366-
elapsed := time.Since(start)
400+
elapsed := time.Since(c.sharedStartTime)
367401
goodput := float64(applicationBytes) / float64(elapsed.Seconds()) * 8 // bps
368-
result := Result{
369-
Elapsed: elapsed,
370-
Goodput: goodput,
371-
Throughput: 0, // TODO
402+
return Result{
403+
Subtest: subtest,
404+
Elapsed: elapsed,
405+
Goodput: goodput,
406+
Throughput: 0, // TODO,
407+
MinRTT: c.minRTT.Load(),
408+
RTT: c.rtt.Load(),
409+
Streams: c.config.NumStreams,
410+
ByteLimit: c.config.ByteLimit,
411+
Length: c.config.Length,
412+
CongestionControl: c.config.CongestionControl,
372413
}
373-
c.config.Emitter.OnResult(result)
374414
}
375415

376416
// Download runs a download test using the settings configured for this client.
@@ -389,6 +429,11 @@ func (c *Throughput1Client) Upload(ctx context.Context) {
389429
}
390430
}
391431

432+
// PrintSummary emits a summary via the configured emitter
433+
func (c *Throughput1Client) PrintSummary() {
434+
c.config.Emitter.OnSummary(c.lastResultForSubtest)
435+
}
436+
392437
func getPathForSubtest(subtest spec.SubtestKind) string {
393438
switch subtest {
394439
case spec.SubtestDownload:

pkg/client/emitter.go

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,12 @@ type Emitter interface {
2020
OnResult(Result)
2121
// OnError is called on errors.
2222
OnError(err error)
23-
// OnComplete is called after a stream completes.
24-
OnComplete(streamID int, server string)
23+
// OnStreamComplete is called after a stream completes.
24+
OnStreamComplete(streamID int, server string)
2525
// OnDebug is called to print debug information.
2626
OnDebug(msg string)
27+
// OnSummary is called to print summary information.
28+
OnSummary(results map[spec.SubtestKind]Result)
2729
}
2830

2931
// HumanReadable prints human-readable output to stdout.
@@ -34,8 +36,8 @@ type HumanReadable struct {
3436

3537
// OnResult prints the aggregate result.
3638
func (HumanReadable) OnResult(r Result) {
37-
fmt.Printf("Elapsed: %.2fs, Goodput: %f Mb/s, MinRTT: %d\n", r.Elapsed.Seconds(),
38-
r.Goodput/1e6, r.MinRTT)
39+
fmt.Printf("%s rate: %f Mb/s, rtt: %.2f, minrtt: %.2f\n",
40+
r.Subtest, r.Goodput/1e6, float32(r.RTT)/1000, float32(r.MinRTT)/1000)
3941
}
4042

4143
// OnStart is called when the stream starts and prints the subtest and server hostname.
@@ -60,11 +62,22 @@ func (HumanReadable) OnError(err error) {
6062
}
6163
}
6264

63-
// OnComplete is called after a stream completes.
64-
func (HumanReadable) OnComplete(streamID int, server string) {
65+
// OnStreamComplete is called after a stream completes.
66+
func (HumanReadable) OnStreamComplete(streamID int, server string) {
6567
fmt.Printf("Stream %d complete (server %s)\n", streamID, server)
6668
}
6769

70+
func (HumanReadable) OnSummary(results map[spec.SubtestKind]Result) {
71+
fmt.Println()
72+
fmt.Printf("Test results:\n")
73+
for kind, result := range results {
74+
fmt.Printf(" %s rate: %.2f Mb/s, rtt: %.2fms, minrtt: %.2fms\n",
75+
kind, result.Goodput/1e6, float32(result.RTT)/1000, float32(result.MinRTT)/1000)
76+
fmt.Printf(" streams: %d, duration: %.2fs, cc algo: %s, byte limit: %d bytes\n",
77+
result.Streams, result.Length.Seconds(), result.CongestionControl, result.ByteLimit)
78+
}
79+
}
80+
6881
// OnDebug is called to print debug information.
6982
func (e HumanReadable) OnDebug(msg string) {
7083
if e.Debug {

0 commit comments

Comments
 (0)