net/speedtest: retune to meet iperf on localhost in a VM

- removed some in-flow time calls
- increase buffer size to 2MB to overcome syscall cost
- move relative time computation from record to report time

Signed-off-by: James Tucker <james@tailscale.com>
pull/5736/head
James Tucker 2 years ago committed by James Tucker
parent 146f51ce76
commit f7cb535693

@ -110,11 +110,12 @@ func runSpeedtest(ctx context.Context, args []string) error {
w := tabwriter.NewWriter(os.Stdout, 12, 0, 0, ' ', tabwriter.TabIndent) w := tabwriter.NewWriter(os.Stdout, 12, 0, 0, ' ', tabwriter.TabIndent)
fmt.Println("Results:") fmt.Println("Results:")
fmt.Fprintln(w, "Interval\t\tTransfer\t\tBandwidth\t\t") fmt.Fprintln(w, "Interval\t\tTransfer\t\tBandwidth\t\t")
startTime := results[0].IntervalStart
for _, r := range results { for _, r := range results {
if r.Total { if r.Total {
fmt.Fprintln(w, "-------------------------------------------------------------------------") fmt.Fprintln(w, "-------------------------------------------------------------------------")
} }
fmt.Fprintf(w, "%.2f-%.2f\tsec\t%.4f\tMBits\t%.4f\tMbits/sec\t\n", r.IntervalStart.Seconds(), r.IntervalEnd.Seconds(), r.MegaBits(), r.MBitsPerSecond()) fmt.Fprintf(w, "%.2f-%.2f\tsec\t%.4f\tMBits\t%.4f\tMbits/sec\t\n", r.IntervalStart.Sub(startTime).Seconds(), r.IntervalEnd.Sub(startTime).Seconds(), r.MegaBits(), r.MBitsPerSecond())
} }
w.Flush() w.Flush()
return nil return nil

@ -11,11 +11,11 @@ import (
) )
const ( const (
blockSize = 32000 // size of the block of data to send blockSize = 2 * 1024 * 1024 // size of the block of data to send
MinDuration = 5 * time.Second // minimum duration for a test MinDuration = 5 * time.Second // minimum duration for a test
DefaultDuration = MinDuration // default duration for a test DefaultDuration = MinDuration // default duration for a test
MaxDuration = 30 * time.Second // maximum duration for a test MaxDuration = 30 * time.Second // maximum duration for a test
version = 1 // value used when comparing client and server versions version = 2 // value used when comparing client and server versions
increment = time.Second // increment to display results for, in seconds increment = time.Second // increment to display results for, in seconds
minInterval = 10 * time.Millisecond // minimum interval length for a result to be included minInterval = 10 * time.Millisecond // minimum interval length for a result to be included
DefaultPort = 20333 DefaultPort = 20333
@ -38,13 +38,13 @@ type configResponse struct {
// This represents the Result of a speedtest within a specific interval // This represents the Result of a speedtest within a specific interval
type Result struct { type Result struct {
Bytes int // number of bytes sent/received during the interval Bytes int // number of bytes sent/received during the interval
IntervalStart time.Duration // duration between the start of the interval and the start of the test IntervalStart time.Time // start of the interval
IntervalEnd time.Duration // duration between the end of the interval and the start of the test IntervalEnd time.Time // end of the interval
Total bool // if true, this result struct represents the entire test, rather than a segment of the test Total bool // if true, this result struct represents the entire test, rather than a segment of the test
} }
func (r Result) MBitsPerSecond() float64 { func (r Result) MBitsPerSecond() float64 {
return r.MegaBits() / (r.IntervalEnd - r.IntervalStart).Seconds() return r.MegaBits() / r.IntervalEnd.Sub(r.IntervalStart).Seconds()
} }
func (r Result) MegaBytes() float64 { func (r Result) MegaBytes() float64 {
@ -56,7 +56,7 @@ func (r Result) MegaBits() float64 {
} }
func (r Result) Interval() time.Duration { func (r Result) Interval() time.Duration {
return r.IntervalEnd - r.IntervalStart return r.IntervalEnd.Sub(r.IntervalStart)
} }
type Direction int type Direction int

@ -81,9 +81,6 @@ func doTest(conn net.Conn, conf config) ([]Result, error) {
var currentTime time.Time var currentTime time.Time
var results []Result var results []Result
startTime := time.Now()
lastCalculated := startTime
if conf.Direction == Download { if conf.Direction == Download {
conn.SetReadDeadline(time.Now().Add(conf.TestDuration).Add(5 * time.Second)) conn.SetReadDeadline(time.Now().Add(conf.TestDuration).Add(5 * time.Second))
} else { } else {
@ -94,6 +91,9 @@ func doTest(conn net.Conn, conf config) ([]Result, error) {
} }
startTime := time.Now()
lastCalculated := startTime
SpeedTestLoop: SpeedTestLoop:
for { for {
var n int var n int
@ -110,48 +110,37 @@ SpeedTestLoop:
return nil, fmt.Errorf("unexpected error has occurred: %w", err) return nil, fmt.Errorf("unexpected error has occurred: %w", err)
} }
} else { } else {
// Need to change the data a little bit, to avoid any compression.
for i := range bufferData {
bufferData[i]++
}
n, err = conn.Write(bufferData) n, err = conn.Write(bufferData)
if err != nil { if err != nil {
// If the write failed, there is most likely something wrong with the connection. // If the write failed, there is most likely something wrong with the connection.
return nil, fmt.Errorf("upload failed: %w", err) return nil, fmt.Errorf("upload failed: %w", err)
} }
} }
currentTime = time.Now()
intervalBytes += n intervalBytes += n
currentTime = time.Now()
// checks if the current time is more or equal to the lastCalculated time plus the increment // checks if the current time is more or equal to the lastCalculated time plus the increment
if currentTime.After(lastCalculated.Add(increment)) { if currentTime.Sub(lastCalculated) >= increment {
intervalStart := lastCalculated.Sub(startTime) results = append(results, Result{Bytes: intervalBytes, IntervalStart: lastCalculated, IntervalEnd: currentTime, Total: false})
intervalEnd := currentTime.Sub(startTime)
if (intervalEnd - intervalStart) > minInterval {
results = append(results, Result{Bytes: intervalBytes, IntervalStart: intervalStart, IntervalEnd: intervalEnd, Total: false})
}
lastCalculated = currentTime lastCalculated = currentTime
totalBytes += intervalBytes totalBytes += intervalBytes
intervalBytes = 0 intervalBytes = 0
} }
if conf.Direction == Upload && time.Since(startTime) > conf.TestDuration { if conf.Direction == Upload && currentTime.Sub(startTime) > conf.TestDuration {
break SpeedTestLoop break SpeedTestLoop
} }
} }
// get last segment // get last segment
intervalStart := lastCalculated.Sub(startTime) if currentTime.Sub(lastCalculated) > minInterval {
intervalEnd := currentTime.Sub(startTime) results = append(results, Result{Bytes: intervalBytes, IntervalStart: lastCalculated, IntervalEnd: currentTime, Total: false})
if (intervalEnd - intervalStart) > minInterval {
results = append(results, Result{Bytes: intervalBytes, IntervalStart: intervalStart, IntervalEnd: intervalEnd, Total: false})
} }
// get total // get total
totalBytes += intervalBytes totalBytes += intervalBytes
intervalEnd = currentTime.Sub(startTime) if currentTime.Sub(startTime) > minInterval {
if intervalEnd > minInterval { results = append(results, Result{Bytes: totalBytes, IntervalStart: startTime, IntervalEnd: currentTime, Total: true})
results = append(results, Result{Bytes: totalBytes, IntervalStart: 0, IntervalEnd: intervalEnd, Total: true})
} }
return results, nil return results, nil

@ -7,6 +7,7 @@ package speedtest
import ( import (
"net" "net"
"testing" "testing"
"time"
) )
func TestDownload(t *testing.T) { func TestDownload(t *testing.T) {
@ -23,9 +24,9 @@ func TestDownload(t *testing.T) {
type state struct { type state struct {
err error err error
} }
displayResult := func(t *testing.T, r Result) { displayResult := func(t *testing.T, r Result, start time.Time) {
t.Helper() t.Helper()
t.Logf("{ Megabytes: %.2f, Start: %.1f, End: %.1f, Total: %t }", r.MegaBytes(), r.IntervalStart.Seconds(), r.IntervalEnd.Seconds(), r.Total) t.Logf("{ Megabytes: %.2f, Start: %.1f, End: %.1f, Total: %t }", r.MegaBytes(), r.IntervalStart.Sub(start).Seconds(), r.IntervalEnd.Sub(start).Seconds(), r.Total)
} }
stateChan := make(chan state, 1) stateChan := make(chan state, 1)
@ -49,8 +50,9 @@ func TestDownload(t *testing.T) {
t.Fatalf("download results: expected length: %d, actual length: %d", expectedLen, len(results)) t.Fatalf("download results: expected length: %d, actual length: %d", expectedLen, len(results))
} }
start := results[0].IntervalStart
for _, result := range results { for _, result := range results {
displayResult(t, result) displayResult(t, result, start)
} }
}) })
@ -66,8 +68,9 @@ func TestDownload(t *testing.T) {
t.Fatalf("upload results: expected length: %d, actual length: %d", expectedLen, len(results)) t.Fatalf("upload results: expected length: %d, actual length: %d", expectedLen, len(results))
} }
start := results[0].IntervalStart
for _, result := range results { for _, result := range results {
displayResult(t, result) displayResult(t, result, start)
} }
}) })

Loading…
Cancel
Save