Skip to content

Commit 632c311

Browse files
committed
integration pprof into go-control-plane for benchmarking and resource
profiling Signed-off-by: Alec Holmes <[email protected]>
1 parent 72157d3 commit 632c311

File tree

14 files changed

+338
-47
lines changed

14 files changed

+338
-47
lines changed

Diff for: .gitignore

+6
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,9 @@ examples/dyplomat/dyplomat
1616
*_gen_test.go
1717

1818
/envoy*.log
19+
20+
# Benchmarking artifacts
21+
*.gz
22+
*.pprof
23+
/benchmarks/reports
24+
/benchmarks/pngs

Diff for: Makefile

+17-2
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
# Variables
55
#------------------------------------------------------------------------------
66

7-
SHELL := /bin/bash
8-
BINDIR := bin
7+
SHELL := /bin/bash
8+
BINDIR := bin
99
PKG := github.com/envoyproxy/go-control-plane
1010

1111
.PHONY: build
@@ -19,6 +19,9 @@ clean:
1919
@go mod tidy
2020
@rm -rf $(BINDIR)
2121
@rm -rf *.log
22+
@rm -rf *.pprof
23+
@rm -rf benchmarks/reports
24+
@rm -rf benchmarks/pngs
2225

2326
# TODO(mattklein123): See the note in TestLinearConcurrentSetWatch() for why we set -parallel here
2427
# This should be removed.
@@ -79,6 +82,18 @@ integration.xds.delta: $(BINDIR)/test $(BINDIR)/upstream
7982
integration.ads.delta: $(BINDIR)/test $(BINDIR)/upstream
8083
env XDS=delta-ads build/integration.sh
8184

85+
86+
#------------------------------------------------------------------------------
87+
# Benchmarks utilizing intergration tests
88+
#------------------------------------------------------------------------------
89+
.PHONY: benchmark docker_benchmarks
90+
benchmark: $(BINDIR)/test $(BINDIR)/upstream
91+
env XDS=xds build/integration.sh
92+
93+
docker_benchmarks:
94+
docker build --pull -f Dockerfile.ci . -t gcp_ci && \
95+
docker run -v $$(pwd):/go-control-plane $$(tty -s && echo "-it" || echo) gcp_ci /bin/bash -c /go-control-plane/build/do_benchmarks.sh
96+
8297
#--------------------------------------
8398
#-- example xDS control plane server
8499
#--------------------------------------

Diff for: benchmarks/README.md

+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# Benchmarks
2+
3+
These benchmarks are designed to run off the integration test suite, and provide detailed profiling artifacts generated with pprof.
4+
5+
## Prerequisites
6+
- [Go 1.16+](https://golang.org/dl/)
7+
- [Graphviz](https://graphviz.org/download/)
8+
- [pprof](https://github.com/google/pprof)
9+
- [Docker](https://www.docker.com/)
10+
11+
## Running Locally
12+
13+
To run the benchmarks locally, we take a similar apprach to the integration tests.
14+
15+
### Requirements
16+
17+
* Envoy binary `envoy` available: set `ENVOY` environment variable to the
18+
location of the binary, or use the default value `/usr/local/bin/envoy`
19+
* `go-control-plane` builds successfully
20+
* Local installation of pprof and graphviz for profiler output
21+
22+
### Steps
23+
24+
To run the benchmark:
25+
```
26+
make benchmark MODE=0
27+
```
28+
29+
There are 5 different modes all corresponding to various profiling outputs:
30+
```go
31+
const (
32+
PPROF_CPU int = iota
33+
PPROF_HEAP
34+
PPROF_MUTEX
35+
PPROF_BLOCK
36+
PPROF_GOROUTINE
37+
)
38+
```
39+
To specifiy the mode, use `MODE` environment variable that corresponds to the output you wish to evaluate.
40+
41+
The output file will be stored at the project root with .pprof extension (e.g. `cpu.pprof`).
42+
43+
Run `pprof` tool like so which will open up a shell. From there, you can run command such as `web` to visualize the result:
44+
45+
```bash
46+
go tool pprof cpu.pprof
47+
(pprof) web
48+
```
49+
For more information, run `help`.
50+
51+
## Running With Docker
52+
53+
To run the benchmarks, we just require the prerequisite of docker and go.
54+
55+
### Steps
56+
57+
To run the benchmarks:
58+
59+
```
60+
make docker_benchmarks
61+
```
62+
63+
This will generate all profile artifacts in the `./benchmarks/reports`. Graphical profile anaylsis is located in `/.benchmarks/pngs`.
64+
65+
For more information on how to interpret these reports/graphs, [click here](https://github.com/google/pprof/blob/master/doc/README.md#graphical-reports)

Diff for: benchmarks/client/README.md

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Benchmark Client
2+
3+
This test client provides simulation of various workloads when communicating with the go-control-plane management server.

Diff for: benchmarks/client/main.go

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package main
2+
3+
import (
4+
"log"
5+
"os"
6+
"runtime"
7+
"time"
8+
9+
"github.com/envoyproxy/go-control-plane/benchmarks/client/xds"
10+
"github.com/urfave/cli/v2"
11+
)
12+
13+
func main() {
14+
// Statically set the max procs
15+
runtime.GOMAXPROCS(runtime.NumCPU())
16+
17+
app := &cli.App{
18+
Name: "xds-benchmark",
19+
Usage: "xds benchmarking tool that simulates client workload",
20+
Commands: []*cli.Command{
21+
{
22+
Name: "run",
23+
Aliases: []string{"r"},
24+
Usage: "run the benchmark with the provided duration",
25+
Action: func(c *cli.Context) error {
26+
arg := c.Args().First()
27+
dur, err := time.ParseDuration(arg)
28+
if err != nil {
29+
return err
30+
}
31+
32+
sess, err := xds.NewSession("localhost:50000")
33+
if err != nil {
34+
return err
35+
}
36+
37+
return sess.Simulate(dur)
38+
},
39+
},
40+
},
41+
}
42+
43+
err := app.Run(os.Args)
44+
if err != nil {
45+
log.Fatal(err)
46+
}
47+
}

Diff for: benchmarks/client/xds/opts.go

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package xds
2+
3+
// Options are configuration settings for the discovery object
4+
type Options struct {
5+
NodeID string
6+
Zone string
7+
Cluster string
8+
ResourceNames []string // List of Envoy resource names to subscribe to
9+
ResourceType string // ex: type.googleapis.com/envoy.api.v2.ClusterLoadAssignment
10+
}
11+
12+
// Option follows the functional opts pattern
13+
type Option func(*Options)
14+
15+
// WithNode will inject the node id into the configuration object
16+
func WithNode(id string) Option {
17+
return func(o *Options) {
18+
o.NodeID = id
19+
}
20+
}
21+
22+
// WithZone will specificy which zone to use in the xDS discovery request
23+
func WithZone(zone string) Option {
24+
return func(o *Options) {
25+
o.Zone = zone
26+
}
27+
}
28+
29+
// WithCluster will specificy which cluster the request is announcing as
30+
func WithCluster(cluster string) Option {
31+
return func(o *Options) {
32+
o.Cluster = cluster
33+
}
34+
}
35+
36+
// WithResourceNames will inject a list of resources the user wants to place watches on
37+
func WithResourceNames(names []string) Option {
38+
return func(o *Options) {
39+
o.ResourceNames = names
40+
}
41+
}
42+
43+
// WithResourceType will inject the specific resource type that a user wants to stream
44+
func WithResourceType(resource string) Option {
45+
return func(o *Options) {
46+
o.ResourceType = resource
47+
}
48+
}

Diff for: benchmarks/client/xds/xds.go

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package xds
2+
3+
import (
4+
"time"
5+
6+
"google.golang.org/grpc"
7+
)
8+
9+
// Sess holds a grpc connection as well as config options to use during the simulation
10+
type Sess struct {
11+
Session *grpc.ClientConn
12+
Opts Options
13+
}
14+
15+
// NewSession will dial a new benchmarking session with the configured options
16+
func NewSession(url string, opts ...Option) (*Sess, error) {
17+
var options Options
18+
for _, o := range opts {
19+
o(&options)
20+
}
21+
22+
conn, err := grpc.Dial(url, grpc.WithBlock(), grpc.WithInsecure())
23+
if err != nil {
24+
return nil, err
25+
}
26+
27+
return &Sess{
28+
Session: conn,
29+
Opts: options,
30+
}, nil
31+
}
32+
33+
// Simulate will start an xDS stream which provides simulatest clients communicating with an xDS server
34+
func (s *Sess) Simulate(target time.Duration) error {
35+
// Create a loop that will continually do work until the elapsed time as passed
36+
for timeout := time.After(target); ; {
37+
select {
38+
case <-timeout:
39+
return nil
40+
default:
41+
// Do some work
42+
43+
}
44+
}
45+
}

Diff for: benchmarks/client/xds/xds_test.go

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
package xds

Diff for: build/do_benchmarks.sh

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
#!/usr/bin/env bash
2+
3+
set -e
4+
set -x
5+
6+
go install golang.org/x/tools/cmd/goimports@latest
7+
go get -u github.com/google/pprof
8+
apt update && apt install -y graphviz
9+
10+
cd /go-control-plane
11+
12+
NUM_DIFF_LINES=`/go/bin/goimports -local github.com/envoyproxy/go-control-plane -d pkg | wc -l`
13+
if [[ $NUM_DIFF_LINES > 0 ]]; then
14+
echo "Failed format check. Run make format"
15+
exit 1
16+
fi
17+
18+
make benchmark MODE=0
19+
make benchmark MODE=1
20+
make benchmark MODE=2
21+
make benchmark MODE=3
22+
make benchmark MODE=4
23+
24+
mkdir -p benchmarks/reports
25+
mkdir -p benchmarks/pngs
26+
27+
# generate our consumable pprof profiles
28+
pprof -text bin/test cpu.pprof > benchmarks/reports/cpu_text.txt
29+
pprof -tree bin/test cpu.pprof > benchmarks/reports/cpu_tree.txt
30+
pprof -traces bin/test cpu.pprof > benchmarks/reports/cpu_trace.txt
31+
32+
pprof -text bin/test goroutine.pprof > benchmarks/reports/goroutine_text.txt
33+
pprof -tree bin/test goroutine.pprof > benchmarks/reports/goroutine_tree.txt
34+
pprof -traces bin/test goroutine.pprof > benchmarks/reports/goroutine_trace.txt
35+
36+
pprof -text bin/test block.pprof > benchmarks/reports/block_text.txt
37+
pprof -tree bin/test block.pprof > benchmarks/reports/block_tree.txt
38+
pprof -traces bin/test block.pprof > benchmarks/reports/block_trace.txt
39+
40+
pprof -text bin/test mutex.pprof > benchmarks/reports/mutex_text.txt
41+
pprof -tree bin/test mutex.pprof > benchmarks/reports/mutex_tree.txt
42+
pprof -traces bin/test mutex.pprof > benchmarks/reports/mutex_trace.txt
43+
44+
pprof -text bin/test mem.pprof > benchmarks/reports/mem_text.txt
45+
pprof -tree bin/test mem.pprof > benchmarks/reports/mem_tree.txt
46+
pprof -traces bin/test mem.pprof > benchmarks/reports/mem_trace.txt
47+
48+
# generate some cool pprof graphs
49+
pprof -png bin/test cpu.pprof > benchmarks/pngs/cpu_graph.png
50+
pprof -png bin/test goroutine.pprof > benchmarks/pngs/goroutine_graph.png
51+
pprof -png bin/test block.pprof > benchmarks/pngs/block_graph.png
52+
pprof -png bin/test mutex.pprof > benchmarks/pngs/mutex_graph.png
53+
pprof -png bin/test mem.pprof > benchmarks/pngs/mem_graph.png

Diff for: build/integration.sh

+3-3
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ MESSAGE=$'Hi, there!\n'
1313
# Management server type. Valid values are "ads", "xds", "rest", "delta", or "delta-ads"
1414
XDS=${XDS:-ads}
1515

16-
# pprof profiler. True means turn on profiling
17-
PPROF=${PPROF:-false}
16+
# pprof profiler mode
17+
MODE=${MODE:-0}
1818

1919
# Number of RTDS layers.
2020
if [ "$XDS" = "ads" ]; then
@@ -43,4 +43,4 @@ function cleanup() {
4343
trap cleanup EXIT
4444

4545
# run the test suite (which also contains the control plane)
46-
bin/test --xds=${XDS} --runtimes=${RUNTIMES} --pprof=${PPROF} -debug -message="$MESSAGE" "$@"
46+
bin/test --xds=${XDS} --runtimes=${RUNTIMES} --pprofMode=${MODE} -debug -message="$MESSAGE" "$@"

Diff for: go.mod

+7
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,24 @@ require (
88
github.com/envoyproxy/protoc-gen-validate v0.9.1
99
github.com/golang/protobuf v1.5.2
1010
github.com/google/go-cmp v0.5.9
11+
github.com/pkg/profile v1.7.0
1112
github.com/prometheus/client_model v0.3.0
1213
github.com/stretchr/testify v1.8.1
14+
github.com/urfave/cli/v2 v2.24.1
1315
go.opentelemetry.io/proto/otlp v0.19.0
1416
google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6
1517
google.golang.org/grpc v1.52.0
1618
google.golang.org/protobuf v1.28.1
1719
)
1820

1921
require (
22+
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
2023
github.com/davecgh/go-spew v1.1.1 // indirect
24+
github.com/felixge/fgprof v0.9.3 // indirect
25+
github.com/google/pprof v0.0.0-20211214055906-6f57359322fd // indirect
2126
github.com/pmezard/go-difflib v1.0.0 // indirect
27+
github.com/russross/blackfriday/v2 v2.1.0 // indirect
28+
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
2229
golang.org/x/net v0.4.0 // indirect
2330
golang.org/x/sys v0.3.0 // indirect
2431
golang.org/x/text v0.5.0 // indirect

0 commit comments

Comments
 (0)