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
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…
Reference in New Issue