Add net benchmark harness

parent 74c14628
......@@ -189,6 +189,7 @@ google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoA
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1 h1:Hz2g2wirWK7H0qIIhGIqRGTuMwTE8HEKFnDZZ7lm9NU=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
......
package tnet
import (
"context"
"fmt"
"net"
"runtime"
"sync"
"testing"
"time"
"google.golang.org/grpc/benchmark/latency"
)
// NetworkTestFunc is a benchmark function under test by `FindNetworkLimit`
type NetworkTestFunc func(b *testing.B, n1, n2 net.Conn)
// ConnectionForNetwork generates a pair of network connections with a specified latency.
func ConnectionForNetwork(n *latency.Network) (n1, n2 net.Conn, err error) {
var wg sync.WaitGroup
wg.Add(1)
var listener net.Listener
listener, err = net.Listen("tcp", "127.0.0.1:0")
if err != nil {
return
}
slowListener := n.Listener(listener)
go func() {
defer wg.Done()
n2, _ = slowListener.Accept()
slowListener.Close()
return
}()
baseDialer := net.Dialer{}
dialer := n.ContextDialer(baseDialer.DialContext)
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
n1, err = dialer(ctx, "tcp4", slowListener.Addr().String())
if err != nil {
return
}
wg.Wait()
return
}
// FindNetworkLimit benchmarks a function to analyze CPU, parallism, and network relationship
func FindNetworkLimit(testFunc NetworkTestFunc) (float64, error) {
maxProcs := runtime.GOMAXPROCS(0)
runtime.GOMAXPROCS(maxProcs)
if maxProcs > 1 {
runtime.GOMAXPROCS(1)
}
network := latency.Network{
Kbps: 0,
Latency: 0,
}
wrapperFunc := func(sb *testing.B) {
n1, n2, err := ConnectionForNetwork(&network)
if err != nil {
sb.Error(err)
}
defer n1.Close()
defer n2.Close()
testFunc(sb, n1, n2)
}
result := testing.Benchmark(wrapperFunc)
if result.N < 1 {
return 0.0, fmt.Errorf("failed to run benchmark")
}
max := (float64(result.Bytes) * float64(result.N) / 1e6) / result.T.Seconds()
fmt.Printf("CPU Bound Limit: %s\n", result)
current := max
network.Latency = 500 * time.Microsecond
for current > max*0.9 {
network.Latency *= 2
result = testing.Benchmark(wrapperFunc)
current = (float64(result.Bytes) * float64(result.N) / 1e6) / result.T.Seconds()
}
fmt.Printf("Latency Bound Limit: %s\n", network.Latency)
network.Kbps = 1024 * 100 // 100Mbps
network.Latency /= 2
for current > max*0.9 {
network.Kbps /= 2
result = testing.Benchmark(wrapperFunc)
current = (float64(result.Bytes) * float64(result.N) / 1e6) / result.T.Seconds()
}
fmt.Printf("Bandwidth Bound Limit: %dKbps\n", network.Kbps)
fmt.Printf("Network Bound Limit: %s\n", result)
// Now, at a network-bounded level (divide bandwidth and latency by 10 as base)
// look at utilizations as a function of parallelism.
network.Latency *= 10
network.Kbps /= 10
prevBytes := int64(-1)
ratio := 1.0
for i := 1; i <= maxProcs; i *= 2 {
runtime.GOMAXPROCS(i)
result = testing.Benchmark(wrapperFunc)
if prevBytes > 0 {
ratio = float64(result.Bytes) / float64(prevBytes)
}
prevBytes = result.Bytes
fmt.Printf("At MaxProc %d %dKbps / %s latency: %s\n", i, network.Kbps, network.Latency, result)
}
fmt.Printf("Slowdown is %f%%\n", 100*(1.0-ratio))
return (1.0 - ratio), nil
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment