summaryrefslogtreecommitdiffhomepage
path: root/test/benchmarks
diff options
context:
space:
mode:
Diffstat (limited to 'test/benchmarks')
-rw-r--r--test/benchmarks/base/size_test.go1
-rw-r--r--test/benchmarks/base/startup_test.go3
-rw-r--r--test/benchmarks/base/sysbench_test.go11
-rw-r--r--test/benchmarks/database/redis_test.go14
-rw-r--r--test/benchmarks/fs/bazel_test.go43
-rw-r--r--test/benchmarks/fs/fio_test.go15
-rw-r--r--test/benchmarks/network/BUILD11
-rw-r--r--test/benchmarks/network/httpd_test.go148
-rw-r--r--test/benchmarks/network/nginx_test.go137
-rw-r--r--test/benchmarks/network/node_test.go16
-rw-r--r--test/benchmarks/network/ruby_test.go15
-rw-r--r--test/benchmarks/network/static_server.go87
-rw-r--r--test/benchmarks/tcp/tcp_proxy.go23
-rw-r--r--test/benchmarks/tools/BUILD2
-rw-r--r--test/benchmarks/tools/ab.go3
-rw-r--r--test/benchmarks/tools/fio.go4
-rw-r--r--test/benchmarks/tools/hey.go4
-rw-r--r--test/benchmarks/tools/iperf.go2
-rw-r--r--test/benchmarks/tools/meminfo.go2
-rw-r--r--test/benchmarks/tools/parser_util.go101
-rw-r--r--test/benchmarks/tools/redis.go2
-rw-r--r--test/benchmarks/tools/sysbench.go10
22 files changed, 419 insertions, 235 deletions
diff --git a/test/benchmarks/base/size_test.go b/test/benchmarks/base/size_test.go
index 3c1364faf..7d3877459 100644
--- a/test/benchmarks/base/size_test.go
+++ b/test/benchmarks/base/size_test.go
@@ -105,6 +105,7 @@ func BenchmarkSizeNginx(b *testing.B) {
machine: machine,
port: port,
runOpts: runOpts,
+ cmd: []string{"nginx", "-c", "/etc/nginx/nginx_gofer.conf"},
})
defer cleanUpContainers(ctx, servers)
diff --git a/test/benchmarks/base/startup_test.go b/test/benchmarks/base/startup_test.go
index 4628a0a41..c36a544db 100644
--- a/test/benchmarks/base/startup_test.go
+++ b/test/benchmarks/base/startup_test.go
@@ -64,6 +64,7 @@ func BenchmarkStartupNginx(b *testing.B) {
machine: machine,
runOpts: runOpts,
port: 80,
+ cmd: []string{"nginx", "-c", "/etc/nginx/nginx_gofer.conf"},
})
}
@@ -123,8 +124,6 @@ func redisInstance(ctx context.Context, b *testing.B, machine harness.Machine) (
// runServerWorkload runs a server workload defined by 'runOpts' and 'cmd'.
// 'clientMachine' is used to connect to the server on 'serverMachine'.
func runServerWorkload(ctx context.Context, b *testing.B, args serverArgs) {
- b.Helper()
-
b.ResetTimer()
for i := 0; i < b.N; i++ {
if err := func() error {
diff --git a/test/benchmarks/base/sysbench_test.go b/test/benchmarks/base/sysbench_test.go
index 6fb813640..39ced3dab 100644
--- a/test/benchmarks/base/sysbench_test.go
+++ b/test/benchmarks/base/sysbench_test.go
@@ -71,8 +71,15 @@ func BenchmarkSysbench(b *testing.B) {
defer machine.CleanUp()
for _, tc := range testCases {
- b.Run(tc.name, func(b *testing.B) {
-
+ param := tools.Parameter{
+ Name: "testname",
+ Value: tc.name,
+ }
+ name, err := tools.ParametersToName(param)
+ if err != nil {
+ b.Fatalf("Failed to parse params: %v", err)
+ }
+ b.Run(name, func(b *testing.B) {
ctx := context.Background()
sysbench := machine.GetContainer(ctx, b)
defer sysbench.CleanUp(ctx)
diff --git a/test/benchmarks/database/redis_test.go b/test/benchmarks/database/redis_test.go
index 394fce820..02e67154e 100644
--- a/test/benchmarks/database/redis_test.go
+++ b/test/benchmarks/database/redis_test.go
@@ -66,7 +66,15 @@ func BenchmarkRedis(b *testing.B) {
ctx := context.Background()
for _, operation := range operations {
- b.Run(operation, func(b *testing.B) {
+ param := tools.Parameter{
+ Name: "operation",
+ Value: operation,
+ }
+ name, err := tools.ParametersToName(param)
+ if err != nil {
+ b.Fatalf("Failed to parse paramaters: %v", err)
+ }
+ b.Run(name, func(b *testing.B) {
server := serverMachine.GetContainer(ctx, b)
defer server.CleanUp(ctx)
@@ -84,12 +92,12 @@ func BenchmarkRedis(b *testing.B) {
ip, err := serverMachine.IPAddress()
if err != nil {
- b.Fatal("failed to get IP from server: %v", err)
+ b.Fatalf("failed to get IP from server: %v", err)
}
serverPort, err := server.FindPort(ctx, port)
if err != nil {
- b.Fatal("failed to get IP from server: %v", err)
+ b.Fatalf("failed to get IP from server: %v", err)
}
if err = harness.WaitUntilServing(ctx, clientMachine, ip, serverPort); err != nil {
diff --git a/test/benchmarks/fs/bazel_test.go b/test/benchmarks/fs/bazel_test.go
index f4236ba37..56103639d 100644
--- a/test/benchmarks/fs/bazel_test.go
+++ b/test/benchmarks/fs/bazel_test.go
@@ -21,6 +21,7 @@ import (
"gvisor.dev/gvisor/pkg/test/dockerutil"
"gvisor.dev/gvisor/test/benchmarks/harness"
+ "gvisor.dev/gvisor/test/benchmarks/tools"
)
// Note: CleanCache versions of this test require running with root permissions.
@@ -46,23 +47,42 @@ func runBuildBenchmark(b *testing.B, image, workdir, target string) {
// Dimensions here are clean/dirty cache (do or don't drop caches)
// and if the mount on which we are compiling is a tmpfs/bind mount.
benchmarks := []struct {
- name string
clearCache bool // clearCache drops caches before running.
tmpfs bool // tmpfs will run compilation on a tmpfs.
}{
- {name: "CleanCache", clearCache: true, tmpfs: false},
- {name: "DirtyCache", clearCache: false, tmpfs: false},
- {name: "CleanCacheTmpfs", clearCache: true, tmpfs: true},
- {name: "DirtyCacheTmpfs", clearCache: false, tmpfs: true},
+ {clearCache: true, tmpfs: false},
+ {clearCache: false, tmpfs: false},
+ {clearCache: true, tmpfs: true},
+ {clearCache: false, tmpfs: true},
}
for _, bm := range benchmarks {
- b.Run(bm.name, func(b *testing.B) {
+ pageCache := tools.Parameter{
+ Name: "page_cache",
+ Value: "clean",
+ }
+ if bm.clearCache {
+ pageCache.Value = "dirty"
+ }
+
+ filesystem := tools.Parameter{
+ Name: "filesystem",
+ Value: "bind",
+ }
+ if bm.tmpfs {
+ filesystem.Value = "tmpfs"
+ }
+ name, err := tools.ParametersToName(pageCache, filesystem)
+ if err != nil {
+ b.Fatalf("Failed to parse parameters: %v", err)
+ }
+
+ b.Run(name, func(b *testing.B) {
// Grab a container.
ctx := context.Background()
container := machine.GetContainer(ctx, b)
defer container.CleanUp(ctx)
- // Start a container and sleep by an order of b.N.
+ // Start a container and sleep.
if err := container.Spawn(ctx, dockerutil.RunOpts{
Image: image,
}, "sleep", fmt.Sprintf("%d", 1000000)); err != nil {
@@ -70,12 +90,13 @@ func runBuildBenchmark(b *testing.B, image, workdir, target string) {
}
// If we are running on a tmpfs, copy to /tmp which is a tmpfs.
+ prefix := ""
if bm.tmpfs {
if out, err := container.Exec(ctx, dockerutil.ExecOpts{},
"cp", "-r", workdir, "/tmp/."); err != nil {
- b.Fatal("failed to copy directory: %v %s", err, out)
+ b.Fatalf("failed to copy directory: %v (%s)", err, out)
}
- workdir = "/tmp" + workdir
+ prefix = "/tmp"
}
// Restart profiles after the copy.
@@ -94,7 +115,7 @@ func runBuildBenchmark(b *testing.B, image, workdir, target string) {
b.StartTimer()
got, err := container.Exec(ctx, dockerutil.ExecOpts{
- WorkDir: workdir,
+ WorkDir: prefix + workdir,
}, "bazel", "build", "-c", "opt", target)
if err != nil {
b.Fatalf("build failed with: %v", err)
@@ -107,7 +128,7 @@ func runBuildBenchmark(b *testing.B, image, workdir, target string) {
}
// Clean bazel in case we use b.N.
_, err = container.Exec(ctx, dockerutil.ExecOpts{
- WorkDir: workdir,
+ WorkDir: prefix + workdir,
}, "bazel", "clean")
if err != nil {
b.Fatalf("build failed with: %v", err)
diff --git a/test/benchmarks/fs/fio_test.go b/test/benchmarks/fs/fio_test.go
index 65874ed8b..5ca191404 100644
--- a/test/benchmarks/fs/fio_test.go
+++ b/test/benchmarks/fs/fio_test.go
@@ -67,8 +67,19 @@ func BenchmarkFio(b *testing.B) {
for _, fsType := range []mount.Type{mount.TypeBind, mount.TypeTmpfs} {
for _, tc := range testCases {
- testName := strings.Title(tc.Test) + strings.Title(string(fsType))
- b.Run(testName, func(b *testing.B) {
+ operation := tools.Parameter{
+ Name: "operation",
+ Value: tc.Test,
+ }
+ filesystem := tools.Parameter{
+ Name: "filesystem",
+ Value: string(fsType),
+ }
+ name, err := tools.ParametersToName(operation, filesystem)
+ if err != nil {
+ b.Fatalf("Failed to parser paramters: %v", err)
+ }
+ b.Run(name, func(b *testing.B) {
ctx := context.Background()
container := machine.GetContainer(ctx, b)
defer container.CleanUp(ctx)
diff --git a/test/benchmarks/network/BUILD b/test/benchmarks/network/BUILD
index bd3f6245c..472b5c387 100644
--- a/test/benchmarks/network/BUILD
+++ b/test/benchmarks/network/BUILD
@@ -5,8 +5,15 @@ package(licenses = ["notice"])
go_library(
name = "network",
testonly = 1,
- srcs = ["network.go"],
- deps = ["//test/benchmarks/harness"],
+ srcs = [
+ "network.go",
+ "static_server.go",
+ ],
+ deps = [
+ "//pkg/test/dockerutil",
+ "//test/benchmarks/harness",
+ "//test/benchmarks/tools",
+ ],
)
go_test(
diff --git a/test/benchmarks/network/httpd_test.go b/test/benchmarks/network/httpd_test.go
index 336e04c91..8d7d5f750 100644
--- a/test/benchmarks/network/httpd_test.go
+++ b/test/benchmarks/network/httpd_test.go
@@ -14,120 +14,71 @@
package network
import (
- "context"
- "fmt"
+ "strconv"
"testing"
"gvisor.dev/gvisor/pkg/test/dockerutil"
- "gvisor.dev/gvisor/test/benchmarks/harness"
"gvisor.dev/gvisor/test/benchmarks/tools"
)
// see Dockerfile '//images/benchmarks/httpd'.
-var docs = map[string]string{
+var httpdDocs = map[string]string{
"notfound": "notfound",
"1Kb": "latin1k.txt",
"10Kb": "latin10k.txt",
"100Kb": "latin100k.txt",
- "1000Kb": "latin1000k.txt",
"1Mb": "latin1024k.txt",
"10Mb": "latin10240k.txt",
}
-// BenchmarkHttpdConcurrency iterates the concurrency argument and tests
-// how well the runtime under test handles requests in parallel.
-func BenchmarkHttpdConcurrency(b *testing.B) {
- // Grab a machine for the client and server.
- clientMachine, err := h.GetMachine()
- if err != nil {
- b.Fatalf("failed to get client: %v", err)
- }
- defer clientMachine.CleanUp()
-
- serverMachine, err := h.GetMachine()
- if err != nil {
- b.Fatalf("failed to get server: %v", err)
- }
- defer serverMachine.CleanUp()
-
- // The test iterates over client concurrency, so set other parameters.
- concurrency := []int{1, 25, 50, 100, 1000}
-
- for _, c := range concurrency {
- b.Run(fmt.Sprintf("%d", c), func(b *testing.B) {
- hey := &tools.Hey{
- Requests: 10000,
- Concurrency: c,
- Doc: docs["10Kb"],
- }
- runHttpd(b, clientMachine, serverMachine, hey, false /* reverse */)
- })
- }
-}
-
-// BenchmarkHttpdDocSize iterates over different sized payloads, testing how
-// well the runtime handles sending different payload sizes.
-func BenchmarkHttpdDocSize(b *testing.B) {
+// BenchmarkHttpd iterates over different sized payloads and concurrency, testing
+// how well the runtime handles sending different payload sizes.
+func BenchmarkHttpd(b *testing.B) {
benchmarkHttpdDocSize(b, false /* reverse */)
}
-// BenchmarkReverseHttpdDocSize iterates over different sized payloads, testing
+// BenchmarkReverseHttpd iterates over different sized payloads, testing
// how well the runtime handles receiving different payload sizes.
-func BenchmarkReverseHttpdDocSize(b *testing.B) {
+func BenchmarkReverseHttpd(b *testing.B) {
benchmarkHttpdDocSize(b, true /* reverse */)
}
+// benchmarkHttpdDocSize iterates through all doc sizes, running subbenchmarks
+// for each size.
func benchmarkHttpdDocSize(b *testing.B, reverse bool) {
b.Helper()
-
- clientMachine, err := h.GetMachine()
- if err != nil {
- b.Fatalf("failed to get machine: %v", err)
- }
- defer clientMachine.CleanUp()
-
- serverMachine, err := h.GetMachine()
- if err != nil {
- b.Fatalf("failed to get machine: %v", err)
- }
- defer serverMachine.CleanUp()
-
- for name, filename := range docs {
+ for size, filename := range httpdDocs {
concurrency := []int{1, 25, 50, 100, 1000}
for _, c := range concurrency {
- b.Run(fmt.Sprintf("%s_%d", name, c), func(b *testing.B) {
+ fsize := tools.Parameter{
+ Name: "filesize",
+ Value: size,
+ }
+ concurrency := tools.Parameter{
+ Name: "concurrency",
+ Value: strconv.Itoa(c),
+ }
+ name, err := tools.ParametersToName(fsize, concurrency)
+ if err != nil {
+ b.Fatalf("Failed to parse parameters: %v", err)
+ }
+ b.Run(name, func(b *testing.B) {
hey := &tools.Hey{
- Requests: 10000,
+ Requests: c * b.N,
Concurrency: c,
Doc: filename,
}
- runHttpd(b, clientMachine, serverMachine, hey, reverse)
+ runHttpd(b, hey, reverse)
})
}
}
}
-// runHttpd runs a single test run.
-func runHttpd(b *testing.B, clientMachine, serverMachine harness.Machine, hey *tools.Hey, reverse bool) {
- b.Helper()
-
- // Grab a container from the server.
- ctx := context.Background()
- var server *dockerutil.Container
- if reverse {
- server = serverMachine.GetNativeContainer(ctx, b)
- } else {
- server = serverMachine.GetContainer(ctx, b)
- }
-
- defer server.CleanUp(ctx)
-
- // Copy the docs to /tmp and serve from there.
- cmd := "mkdir -p /tmp/html; cp -r /local/* /tmp/html/.; apache2 -X"
+// runHttpd configures the static serving methods to run httpd.
+func runHttpd(b *testing.B, hey *tools.Hey, reverse bool) {
+ // httpd runs on port 80.
port := 80
-
- // Start the server.
- if err := server.Spawn(ctx, dockerutil.RunOpts{
+ httpdRunOpts := dockerutil.RunOpts{
Image: "benchmarks/httpd",
Ports: []int{port},
Env: []string{
@@ -138,44 +89,7 @@ func runHttpd(b *testing.B, clientMachine, serverMachine harness.Machine, hey *t
"APACHE_LOG_DIR=/tmp",
"APACHE_PID_FILE=/tmp/apache.pid",
},
- }, "sh", "-c", cmd); err != nil {
- b.Fatalf("failed to start server: %v", err)
- }
-
- ip, err := serverMachine.IPAddress()
- if err != nil {
- b.Fatalf("failed to find server ip: %v", err)
- }
-
- servingPort, err := server.FindPort(ctx, port)
- if err != nil {
- b.Fatalf("failed to find server port %d: %v", port, err)
- }
-
- // Check the server is serving.
- harness.WaitUntilServing(ctx, clientMachine, ip, servingPort)
-
- var client *dockerutil.Container
- // Grab a client.
- if reverse {
- client = clientMachine.GetContainer(ctx, b)
- } else {
- client = clientMachine.GetNativeContainer(ctx, b)
- }
- defer client.CleanUp(ctx)
-
- b.ResetTimer()
- server.RestartProfiles()
- for i := 0; i < b.N; i++ {
- out, err := client.Run(ctx, dockerutil.RunOpts{
- Image: "benchmarks/hey",
- }, hey.MakeCmd(ip, servingPort)...)
- if err != nil {
- b.Fatalf("run failed with: %v", err)
- }
-
- b.StopTimer()
- hey.Report(b, out)
- b.StartTimer()
}
+ httpdCmd := []string{"sh", "-c", "mkdir -p /tmp/html; cp -r /local/* /tmp/html/.; apache2 -X"}
+ runStaticServer(b, httpdRunOpts, httpdCmd, port, hey, reverse)
}
diff --git a/test/benchmarks/network/nginx_test.go b/test/benchmarks/network/nginx_test.go
index 2bf1a3624..08565d0b2 100644
--- a/test/benchmarks/network/nginx_test.go
+++ b/test/benchmarks/network/nginx_test.go
@@ -14,91 +14,90 @@
package network
import (
- "context"
- "fmt"
+ "strconv"
"testing"
"gvisor.dev/gvisor/pkg/test/dockerutil"
- "gvisor.dev/gvisor/test/benchmarks/harness"
"gvisor.dev/gvisor/test/benchmarks/tools"
)
-// BenchmarkNginxConcurrency iterates the concurrency argument and tests
-// how well the runtime under test handles requests in parallel.
-// TODO(gvisor.dev/issue/3536): Update with different doc sizes like Httpd.
-func BenchmarkNginxConcurrency(b *testing.B) {
- // Grab a machine for the client and server.
- clientMachine, err := h.GetMachine()
- if err != nil {
- b.Fatalf("failed to get client: %v", err)
- }
- defer clientMachine.CleanUp()
+// see Dockerfile '//images/benchmarks/nginx'.
+var nginxDocs = map[string]string{
+ "notfound": "notfound",
+ "1Kb": "latin1k.txt",
+ "10Kb": "latin10k.txt",
+ "100Kb": "latin100k.txt",
+ "1Mb": "latin1024k.txt",
+ "10Mb": "latin10240k.txt",
+}
- serverMachine, err := h.GetMachine()
- if err != nil {
- b.Fatalf("failed to get server: %v", err)
- }
- defer serverMachine.CleanUp()
+// BenchmarkNginxDocSize iterates over different sized payloads, testing how
+// well the runtime handles sending different payload sizes.
+func BenchmarkNginxDocSize(b *testing.B) {
+ benchmarkNginxDocSize(b, false /* reverse */, true /* tmpfs */)
+ benchmarkNginxDocSize(b, false /* reverse */, false /* tmpfs */)
+}
- concurrency := []int{1, 5, 10, 25}
- for _, c := range concurrency {
- b.Run(fmt.Sprintf("%d", c), func(b *testing.B) {
- hey := &tools.Hey{
- Requests: 10000,
- Concurrency: c,
- }
- runNginx(b, clientMachine, serverMachine, hey)
- })
- }
+// BenchmarkReverseNginxDocSize iterates over different sized payloads, testing
+// how well the runtime handles receiving different payload sizes.
+func BenchmarkReverseNginxDocSize(b *testing.B) {
+ benchmarkNginxDocSize(b, true /* reverse */, true /* tmpfs */)
}
-// runHttpd runs a single test run.
-func runNginx(b *testing.B, clientMachine, serverMachine harness.Machine, hey *tools.Hey) {
- b.Helper()
+// benchmarkNginxDocSize iterates through all doc sizes, running subbenchmarks
+// for each size.
+func benchmarkNginxDocSize(b *testing.B, reverse, tmpfs bool) {
+ for size, filename := range nginxDocs {
+ concurrency := []int{1, 25, 50, 100, 1000}
+ for _, c := range concurrency {
+ fsize := tools.Parameter{
+ Name: "filesize",
+ Value: size,
+ }
- // Grab a container from the server.
- ctx := context.Background()
- server := serverMachine.GetContainer(ctx, b)
- defer server.CleanUp(ctx)
+ threads := tools.Parameter{
+ Name: "concurrency",
+ Value: strconv.Itoa(c),
+ }
- port := 80
- // Start the server.
- if err := server.Spawn(ctx,
- dockerutil.RunOpts{
- Image: "benchmarks/nginx",
- Ports: []int{port},
- }); err != nil {
- b.Fatalf("server failed to start: %v", err)
- }
+ fs := tools.Parameter{
+ Name: "filesystem",
+ Value: "bind",
+ }
+ if tmpfs {
+ fs.Value = "tmpfs"
+ }
+ name, err := tools.ParametersToName(fsize, threads, fs)
+ if err != nil {
+ b.Fatalf("Failed to parse parameters: %v", err)
+ }
- ip, err := serverMachine.IPAddress()
- if err != nil {
- b.Fatalf("failed to find server ip: %v", err)
+ b.Run(name, func(b *testing.B) {
+ hey := &tools.Hey{
+ Requests: c * b.N,
+ Concurrency: c,
+ Doc: filename,
+ }
+ runNginx(b, hey, reverse, tmpfs)
+ })
+ }
}
+}
- servingPort, err := server.FindPort(ctx, port)
- if err != nil {
- b.Fatalf("failed to find server port %d: %v", port, err)
+// runNginx configures the static serving methods to run httpd.
+func runNginx(b *testing.B, hey *tools.Hey, reverse, tmpfs bool) {
+ // nginx runs on port 80.
+ port := 80
+ nginxRunOpts := dockerutil.RunOpts{
+ Image: "benchmarks/nginx",
+ Ports: []int{port},
}
- // Check the server is serving.
- harness.WaitUntilServing(ctx, clientMachine, ip, servingPort)
-
- // Grab a client.
- client := clientMachine.GetNativeContainer(ctx, b)
- defer client.CleanUp(ctx)
-
- b.ResetTimer()
- server.RestartProfiles()
- for i := 0; i < b.N; i++ {
- out, err := client.Run(ctx, dockerutil.RunOpts{
- Image: "benchmarks/hey",
- }, hey.MakeCmd(ip, servingPort)...)
- if err != nil {
- b.Fatalf("run failed with: %v", err)
- }
- b.StopTimer()
- hey.Report(b, out)
- b.StartTimer()
+ nginxCmd := []string{"nginx", "-c", "/etc/nginx/nginx_gofer.conf"}
+ if tmpfs {
+ nginxCmd = []string{"sh", "-c", "mkdir -p /tmp/html && cp -a /local/* /tmp/html && nginx -c /etc/nginx/nginx.conf"}
}
+
+ // Command copies nginxDocs to tmpfs serving directory and runs nginx.
+ runStaticServer(b, nginxRunOpts, nginxCmd, port, hey, reverse)
}
diff --git a/test/benchmarks/network/node_test.go b/test/benchmarks/network/node_test.go
index 52eb794c4..254538899 100644
--- a/test/benchmarks/network/node_test.go
+++ b/test/benchmarks/network/node_test.go
@@ -15,7 +15,7 @@ package network
import (
"context"
- "fmt"
+ "strconv"
"testing"
"time"
@@ -31,7 +31,15 @@ import (
func BenchmarkNode(b *testing.B) {
concurrency := []int{1, 5, 10, 25}
for _, c := range concurrency {
- b.Run(fmt.Sprintf("Concurrency%d", c), func(b *testing.B) {
+ param := tools.Parameter{
+ Name: "concurrency",
+ Value: strconv.Itoa(c),
+ }
+ name, err := tools.ParametersToName(param)
+ if err != nil {
+ b.Fatalf("Failed to parse parameters: %v", err)
+ }
+ b.Run(name, func(b *testing.B) {
hey := &tools.Hey{
Requests: b.N * c, // Requests b.N requests per thread.
Concurrency: c,
@@ -48,14 +56,14 @@ func runNode(b *testing.B, hey *tools.Hey) {
// The machine to hold Redis and the Node Server.
serverMachine, err := h.GetMachine()
if err != nil {
- b.Fatal("failed to get machine with: %v", err)
+ b.Fatalf("failed to get machine with: %v", err)
}
defer serverMachine.CleanUp()
// The machine to run 'hey'.
clientMachine, err := h.GetMachine()
if err != nil {
- b.Fatal("failed to get machine with: %v", err)
+ b.Fatalf("failed to get machine with: %v", err)
}
defer clientMachine.CleanUp()
diff --git a/test/benchmarks/network/ruby_test.go b/test/benchmarks/network/ruby_test.go
index 5e0b2b724..0174ff3f3 100644
--- a/test/benchmarks/network/ruby_test.go
+++ b/test/benchmarks/network/ruby_test.go
@@ -16,6 +16,7 @@ package network
import (
"context"
"fmt"
+ "strconv"
"testing"
"time"
@@ -31,7 +32,15 @@ import (
func BenchmarkRuby(b *testing.B) {
concurrency := []int{1, 5, 10, 25}
for _, c := range concurrency {
- b.Run(fmt.Sprintf("Concurrency%d", c), func(b *testing.B) {
+ param := tools.Parameter{
+ Name: "concurrency",
+ Value: strconv.Itoa(c),
+ }
+ name, err := tools.ParametersToName(param)
+ if err != nil {
+ b.Fatalf("Failed to parse parameters: %v", err)
+ }
+ b.Run(name, func(b *testing.B) {
hey := &tools.Hey{
Requests: b.N * c, // b.N requests per thread.
Concurrency: c,
@@ -47,14 +56,14 @@ func runRuby(b *testing.B, hey *tools.Hey) {
// The machine to hold Redis and the Ruby Server.
serverMachine, err := h.GetMachine()
if err != nil {
- b.Fatal("failed to get machine with: %v", err)
+ b.Fatalf("failed to get machine with: %v", err)
}
defer serverMachine.CleanUp()
// The machine to run 'hey'.
clientMachine, err := h.GetMachine()
if err != nil {
- b.Fatal("failed to get machine with: %v", err)
+ b.Fatalf("failed to get machine with: %v", err)
}
defer clientMachine.CleanUp()
ctx := context.Background()
diff --git a/test/benchmarks/network/static_server.go b/test/benchmarks/network/static_server.go
new file mode 100644
index 000000000..e747a1395
--- /dev/null
+++ b/test/benchmarks/network/static_server.go
@@ -0,0 +1,87 @@
+// Copyright 2020 The gVisor Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package network
+
+import (
+ "context"
+ "testing"
+
+ "gvisor.dev/gvisor/pkg/test/dockerutil"
+ "gvisor.dev/gvisor/test/benchmarks/harness"
+ "gvisor.dev/gvisor/test/benchmarks/tools"
+)
+
+// runStaticServer runs static serving workloads (httpd, nginx).
+func runStaticServer(b *testing.B, serverOpts dockerutil.RunOpts, serverCmd []string, port int, hey *tools.Hey, reverse bool) {
+ ctx := context.Background()
+
+ // Get two machines: a client and server.
+ clientMachine, err := h.GetMachine()
+ if err != nil {
+ b.Fatalf("failed to get machine: %v", err)
+ }
+ defer clientMachine.CleanUp()
+
+ serverMachine, err := h.GetMachine()
+ if err != nil {
+ b.Fatalf("failed to get machine: %v", err)
+ }
+ defer serverMachine.CleanUp()
+
+ // Make the containers. 'reverse=true' specifies that the client should use the
+ // runtime under test.
+ var client, server *dockerutil.Container
+ if reverse {
+ client = clientMachine.GetContainer(ctx, b)
+ server = serverMachine.GetNativeContainer(ctx, b)
+ } else {
+ client = clientMachine.GetNativeContainer(ctx, b)
+ server = serverMachine.GetContainer(ctx, b)
+ }
+ defer client.CleanUp(ctx)
+ defer server.CleanUp(ctx)
+
+ // Start the server.
+ if err := server.Spawn(ctx, serverOpts, serverCmd...); err != nil {
+ b.Fatalf("failed to start server: %v", err)
+ }
+
+ // Get its IP.
+ ip, err := serverMachine.IPAddress()
+ if err != nil {
+ b.Fatalf("failed to find server ip: %v", err)
+ }
+
+ // Get the published port.
+ servingPort, err := server.FindPort(ctx, port)
+ if err != nil {
+ b.Fatalf("failed to find server port %d: %v", port, err)
+ }
+
+ // Make sure the server is serving.
+ harness.WaitUntilServing(ctx, clientMachine, ip, servingPort)
+ b.ResetTimer()
+ server.RestartProfiles()
+ out, err := client.Run(ctx, dockerutil.RunOpts{
+ Image: "benchmarks/hey",
+ }, hey.MakeCmd(ip, servingPort)...)
+ if err != nil {
+ b.Fatalf("run failed with: %v", err)
+ }
+
+ b.StopTimer()
+ hey.Report(b, out)
+ b.StartTimer()
+}
diff --git a/test/benchmarks/tcp/tcp_proxy.go b/test/benchmarks/tcp/tcp_proxy.go
index 4b7ca7a14..5afe10f69 100644
--- a/test/benchmarks/tcp/tcp_proxy.go
+++ b/test/benchmarks/tcp/tcp_proxy.go
@@ -174,8 +174,8 @@ func newNetstackImpl(mode string) (impl, error) {
}
// Create a new network stack.
- netProtos := []stack.NetworkProtocol{ipv4.NewProtocol(), arp.NewProtocol()}
- transProtos := []stack.TransportProtocol{tcp.NewProtocol(), udp.NewProtocol()}
+ netProtos := []stack.NetworkProtocolFactory{ipv4.NewProtocol, arp.NewProtocol}
+ transProtos := []stack.TransportProtocolFactory{tcp.NewProtocol, udp.NewProtocol}
s := stack.New(stack.Options{
NetworkProtocols: netProtos,
TransportProtocols: transProtos,
@@ -228,19 +228,26 @@ func newNetstackImpl(mode string) (impl, error) {
})
// Set protocol options.
- if err := s.SetTransportProtocolOption(tcp.ProtocolNumber, tcp.SACKEnabled(*sack)); err != nil {
- return nil, fmt.Errorf("SetTransportProtocolOption for SACKEnabled failed: %s", err)
+ {
+ opt := tcpip.TCPSACKEnabled(*sack)
+ if err := s.SetTransportProtocolOption(tcp.ProtocolNumber, &opt); err != nil {
+ return nil, fmt.Errorf("SetTransportProtocolOption(%d, &%T(%t)): %s", tcp.ProtocolNumber, opt, opt, err)
+ }
}
// Enable Receive Buffer Auto-Tuning.
- if err := s.SetTransportProtocolOption(tcp.ProtocolNumber, tcpip.ModerateReceiveBufferOption(*moderateRecvBuf)); err != nil {
- return nil, fmt.Errorf("SetTransportProtocolOption failed: %s", err)
+ {
+ opt := tcpip.TCPModerateReceiveBufferOption(*moderateRecvBuf)
+ if err := s.SetTransportProtocolOption(tcp.ProtocolNumber, &opt); err != nil {
+ return nil, fmt.Errorf("SetTransportProtocolOption(%d, &%T(%t)): %s", tcp.ProtocolNumber, opt, opt, err)
+ }
}
// Set Congestion Control to cubic if requested.
if *cubic {
- if err := s.SetTransportProtocolOption(tcp.ProtocolNumber, tcpip.CongestionControlOption("cubic")); err != nil {
- return nil, fmt.Errorf("SetTransportProtocolOption for CongestionControlOption(cubic) failed: %s", err)
+ opt := tcpip.CongestionControlOption("cubic")
+ if err := s.SetTransportProtocolOption(tcp.ProtocolNumber, &opt); err != nil {
+ return nil, fmt.Errorf("SetTransportProtocolOption(%d, &%T(%s)): %s", tcp.ProtocolNumber, opt, opt, err)
}
}
diff --git a/test/benchmarks/tools/BUILD b/test/benchmarks/tools/BUILD
index e5734d85c..9290830d7 100644
--- a/test/benchmarks/tools/BUILD
+++ b/test/benchmarks/tools/BUILD
@@ -4,12 +4,14 @@ package(licenses = ["notice"])
go_library(
name = "tools",
+ testonly = 1,
srcs = [
"ab.go",
"fio.go",
"hey.go",
"iperf.go",
"meminfo.go",
+ "parser_util.go",
"redis.go",
"sysbench.go",
"tools.go",
diff --git a/test/benchmarks/tools/ab.go b/test/benchmarks/tools/ab.go
index 4cc9c3bce..d9abf0763 100644
--- a/test/benchmarks/tools/ab.go
+++ b/test/benchmarks/tools/ab.go
@@ -46,18 +46,21 @@ func (a *ApacheBench) Report(b *testing.B, output string) {
b.Logf("failed to parse transferrate: %v", err)
}
b.ReportMetric(transferRate*1024, "transfer_rate_b/s") // Convert from Kb/s to b/s.
+ ReportCustomMetric(b, transferRate*1024, "transfer_rate" /*metric name*/, "bytes_per_second" /*unit*/)
latency, err := a.parseLatency(output)
if err != nil {
b.Logf("failed to parse latency: %v", err)
}
b.ReportMetric(latency/1000, "mean_latency_secs") // Convert from ms to s.
+ ReportCustomMetric(b, latency/1000, "mean_latency" /*metric name*/, "s" /*unit*/)
reqPerSecond, err := a.parseRequestsPerSecond(output)
if err != nil {
b.Logf("failed to parse requests per second: %v", err)
}
b.ReportMetric(reqPerSecond, "requests_per_second")
+ ReportCustomMetric(b, reqPerSecond, "requests_per_second" /*metric name*/, "QPS" /*unit*/)
}
var transferRateRE = regexp.MustCompile(`Transfer rate:\s+(\d+\.?\d+?)\s+\[Kbytes/sec\]\s+received`)
diff --git a/test/benchmarks/tools/fio.go b/test/benchmarks/tools/fio.go
index 20000db16..f5f60fa84 100644
--- a/test/benchmarks/tools/fio.go
+++ b/test/benchmarks/tools/fio.go
@@ -56,13 +56,13 @@ func (f *Fio) Report(b *testing.B, output string) {
if err != nil {
b.Fatalf("failed to parse bandwidth from %s with: %v", output, err)
}
- b.ReportMetric(bw, "bandwidth_b/s") // in b/s.
+ ReportCustomMetric(b, bw, "bandwidth" /*metric name*/, "bytes_per_second" /*unit*/)
iops, err := f.parseIOps(output, isRead)
if err != nil {
b.Fatalf("failed to parse iops from %s with: %v", output, err)
}
- b.ReportMetric(iops, "iops")
+ ReportCustomMetric(b, iops, "io_ops" /*metric name*/, "ops_per_second" /*unit*/)
}
// parseBandwidth reports the bandwidth in b/s.
diff --git a/test/benchmarks/tools/hey.go b/test/benchmarks/tools/hey.go
index b1e20e356..b8cb938fe 100644
--- a/test/benchmarks/tools/hey.go
+++ b/test/benchmarks/tools/hey.go
@@ -43,13 +43,13 @@ func (h *Hey) Report(b *testing.B, output string) {
if err != nil {
b.Fatalf("failed to parse requests per second: %v", err)
}
- b.ReportMetric(requests, "requests_per_second")
+ ReportCustomMetric(b, requests, "requests_per_second" /*metric name*/, "QPS" /*unit*/)
ave, err := h.parseAverageLatency(output)
if err != nil {
b.Fatalf("failed to parse average latency: %v", err)
}
- b.ReportMetric(ave, "average_latency_secs")
+ ReportCustomMetric(b, ave, "average_latency" /*metric name*/, "s" /*unit*/)
}
var heyReqPerSecondRE = regexp.MustCompile(`Requests/sec:\s*(\d+\.?\d+?)\s+`)
diff --git a/test/benchmarks/tools/iperf.go b/test/benchmarks/tools/iperf.go
index df3d9349b..5c4e7125b 100644
--- a/test/benchmarks/tools/iperf.go
+++ b/test/benchmarks/tools/iperf.go
@@ -42,7 +42,7 @@ func (i *Iperf) Report(b *testing.B, output string) {
if err != nil {
b.Fatalf("failed to parse bandwitdth from %s: %v", output, err)
}
- b.ReportMetric(bW*1024, "bandwidth_b/s") // Convert from Kb/s to b/s.
+ ReportCustomMetric(b, bW*1024, "bandwidth" /*metric name*/, "bytes_per_second" /*unit*/)
}
// bandwidth parses the Bandwidth number from an iperf report. A sample is below.
diff --git a/test/benchmarks/tools/meminfo.go b/test/benchmarks/tools/meminfo.go
index 2414a96a7..b5786fe11 100644
--- a/test/benchmarks/tools/meminfo.go
+++ b/test/benchmarks/tools/meminfo.go
@@ -45,7 +45,7 @@ func (*Meminfo) Report(b *testing.B, before, after string) {
b.Fatalf("could not parse before value %s: %v", before, err)
}
val := 1024 * ((beforeVal - afterVal) / float64(b.N))
- b.ReportMetric(val, "average_container_size_bytes")
+ ReportCustomMetric(b, val, "average_container_size" /*metric name*/, "bytes" /*units*/)
}
var memInfoRE = regexp.MustCompile(`MemAvailable:\s*(\d+)\skB\n`)
diff --git a/test/benchmarks/tools/parser_util.go b/test/benchmarks/tools/parser_util.go
new file mode 100644
index 000000000..a4555c7dd
--- /dev/null
+++ b/test/benchmarks/tools/parser_util.go
@@ -0,0 +1,101 @@
+// Copyright 2020 The gVisor Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package tools
+
+import (
+ "fmt"
+ "regexp"
+ "strconv"
+ "strings"
+ "testing"
+)
+
+// Parameter is a test parameter.
+type Parameter struct {
+ Name string
+ Value string
+}
+
+// Output is parsed and split by these values. Make them illegal in input methods.
+// We are constrained on what characters these can be by 1) docker's allowable
+// container names, 2) golang allowable benchmark names, and 3) golangs allowable
+// charecters in b.ReportMetric calls.
+var illegalChars = regexp.MustCompile(`[/\.]`)
+
+// ParametersToName joins parameters into a string format for parsing.
+// It is meant to be used for t.Run() calls in benchmark tools.
+func ParametersToName(params ...Parameter) (string, error) {
+ var strs []string
+ for _, param := range params {
+ if illegalChars.MatchString(param.Name) || illegalChars.MatchString(param.Value) {
+ return "", fmt.Errorf("params Name: %q and Value: %q cannot container '.' or '/'", param.Name, param.Value)
+ }
+ strs = append(strs, strings.Join([]string{param.Name, param.Value}, "."))
+ }
+ return strings.Join(strs, "/"), nil
+}
+
+// NameToParameters parses the string created by ParametersToName and returns
+// it as a set of Parameters.
+// Example: BenchmarkRuby/server_threads.1/doc_size.16KB-6
+// The parameter part of this benchmark is:
+// "server_threads.1/doc_size.16KB" (BenchmarkRuby is the name, and 6 is GOMAXPROCS)
+// This function will return a slice with two parameters ->
+// {Name: server_threads, Value: 1}, {Name: doc_size, Value: 16KB}
+func NameToParameters(name string) ([]*Parameter, error) {
+ var params []*Parameter
+ for _, cond := range strings.Split(name, "/") {
+ cs := strings.Split(cond, ".")
+ switch len(cs) {
+ case 1:
+ params = append(params, &Parameter{Name: cond, Value: cond})
+ case 2:
+ params = append(params, &Parameter{Name: cs[0], Value: cs[1]})
+ default:
+ return nil, fmt.Errorf("failed to parse param: %s", cond)
+ }
+ }
+ return params, nil
+}
+
+// ReportCustomMetric reports a metric in a set format for parsing.
+func ReportCustomMetric(b *testing.B, value float64, name, unit string) {
+ if illegalChars.MatchString(name) || illegalChars.MatchString(unit) {
+ b.Fatalf("name: %q and unit: %q cannot contain '/' or '.'", name, unit)
+ }
+ nameUnit := strings.Join([]string{name, unit}, ".")
+ b.ReportMetric(value, nameUnit)
+}
+
+// Metric holds metric data parsed from a string based on the format
+// ReportMetric.
+type Metric struct {
+ Name string
+ Unit string
+ Sample float64
+}
+
+// ParseCustomMetric parses a metric reported with ReportCustomMetric.
+func ParseCustomMetric(value, metric string) (*Metric, error) {
+ sample, err := strconv.ParseFloat(value, 64)
+ if err != nil {
+ return nil, fmt.Errorf("failed to parse value: %v", err)
+ }
+ nameUnit := strings.Split(metric, ".")
+ if len(nameUnit) != 2 {
+ return nil, fmt.Errorf("failed to parse metric: %s", metric)
+ }
+ return &Metric{Name: nameUnit[0], Unit: nameUnit[1], Sample: sample}, nil
+}
diff --git a/test/benchmarks/tools/redis.go b/test/benchmarks/tools/redis.go
index c899ae0d4..e35886437 100644
--- a/test/benchmarks/tools/redis.go
+++ b/test/benchmarks/tools/redis.go
@@ -49,7 +49,7 @@ func (r *Redis) Report(b *testing.B, output string) {
if err != nil {
b.Fatalf("parsing result %s failed with err: %v", output, err)
}
- b.ReportMetric(result, r.Operation) // operations per second
+ ReportCustomMetric(b, result, r.Operation /*metric_name*/, "QPS" /*unit*/)
}
// parseOperation grabs the metric operations per second from redis-benchmark output.
diff --git a/test/benchmarks/tools/sysbench.go b/test/benchmarks/tools/sysbench.go
index 6b2f75ca2..7ccacd8ff 100644
--- a/test/benchmarks/tools/sysbench.go
+++ b/test/benchmarks/tools/sysbench.go
@@ -80,7 +80,7 @@ func (s *SysbenchCPU) Report(b *testing.B, output string) {
if err != nil {
b.Fatalf("parsing CPU events from %s failed: %v", output, err)
}
- b.ReportMetric(result, "cpu_events_per_second")
+ ReportCustomMetric(b, result, "cpu_events" /*metric name*/, "events_per_second" /*unit*/)
}
var cpuEventsPerSecondRE = regexp.MustCompile(`events per second:\s*(\d*.?\d*)\n`)
@@ -144,7 +144,7 @@ func (s *SysbenchMemory) Report(b *testing.B, output string) {
if err != nil {
b.Fatalf("parsing result %s failed with err: %v", output, err)
}
- b.ReportMetric(result, "operations_per_second")
+ ReportCustomMetric(b, result, "memory_operations" /*metric name*/, "ops_per_second" /*unit*/)
}
var memoryOperationsRE = regexp.MustCompile(`Total\soperations:\s+\d*\s*\((\d*\.\d*)\sper\ssecond\)`)
@@ -198,19 +198,19 @@ func (s *SysbenchMutex) Report(b *testing.B, output string) {
if err != nil {
b.Fatalf("parsing result %s failed with err: %v", output, err)
}
- b.ReportMetric(result, "average_execution_time_secs")
+ ReportCustomMetric(b, result, "average_execution_time" /*metric name*/, "s" /*unit*/)
result, err = s.parseDeviation(output)
if err != nil {
b.Fatalf("parsing result %s failed with err: %v", output, err)
}
- b.ReportMetric(result, "stdev_execution_time_secs")
+ ReportCustomMetric(b, result, "stddev_execution_time" /*metric name*/, "s" /*unit*/)
result, err = s.parseLatency(output)
if err != nil {
b.Fatalf("parsing result %s failed with err: %v", output, err)
}
- b.ReportMetric(result/1000, "average_latency_secs")
+ ReportCustomMetric(b, result/1000, "average_latency" /*metric name*/, "s" /*unit*/)
}
var executionTimeRE = regexp.MustCompile(`execution time \(avg/stddev\):\s*(\d*.?\d*)/(\d*.?\d*)`)