sweet: merge tile38 benchmarks into one benchmark with all 3 queries
This change merges the 3 Tile38*Request benchmarks into Tile38QueryLoad.
The purpose of this change is three-fold:
- The existing benchmarks were more microbenchmarks since they really
just hammered a single codepath. Here we at least hammer 3, which is
nice for e.g. PGO experimentation.
- The Tile38 server is now only started up once for a benchmark run.
Previously it was started up 3 times, each time taking around 30
seconds to load state from disk, causing benchmarking to be really
slow.
- This change lets us afford to run a single Tile38 server for longer,
which should help reduce GC-related noise by adding more cycles.
This change increases the number of benchmark iterations from 20*50000
to 40*50000, a 2x increase. While intuitively that seems like it
should make the benchmark only slightly faster, since we're only
spinning up one server for all those iterations, we actually save a lot
more time.
20*50000 iterations was usually ~30 seconds. It took ~30 seconds to
start up the server. For 1 full run it took 3 minutes.
After this change, starting up the server still takes ~30 seconds, but
we only do it once, and the iterations itself only take ~60 seconds.
Now, 1 full run only takes 1 minute 30 seconds, or half as long.
For golang/go#59672.
Change-Id: I5694737e8dde977cbc6013f1d879d0a742a6c895
Reviewed-on: https://go-review.googlesource.com/c/benchmarks/+/485375
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Michael Knyszek <mknyszek@google.com>
Reviewed-by: Michael Pratt <mpratt@google.com>
diff --git a/sweet/benchmarks/tile38/main.go b/sweet/benchmarks/tile38/main.go
index 8de4af2..f346799 100644
--- a/sweet/benchmarks/tile38/main.go
+++ b/sweet/benchmarks/tile38/main.go
@@ -85,8 +85,6 @@
cliCfg.serverProcs = serverProcs
}
-type requestFunc func(redis.Conn, float64, float64) error
-
func doWithinCircle(c redis.Conn, lat, lon float64) error {
_, err := c.Do("WITHIN", "key:bench", "COUNT", "CIRCLE",
strconv.FormatFloat(lat, 'f', 5, 64),
@@ -113,41 +111,48 @@
return err
}
+type requestFunc func(redis.Conn, float64, float64) error
+
+var requestFuncs = []requestFunc{
+ doWithinCircle,
+ doIntersectsCircle,
+ doNearby,
+}
+
func randPoint() (float64, float64) {
return rand.Float64()*180 - 90, rand.Float64()*360 - 180
}
type worker struct {
redis.Conn
- runner requestFunc
iterCount *int64 // Accessed atomically.
lat []time.Duration
}
-func newWorker(host string, port int, req requestFunc, iterCount *int64) (*worker, error) {
+func newWorker(host string, port int, iterCount *int64) (*worker, error) {
conn, err := redis.Dial("tcp", fmt.Sprintf("%s:%d", host, port))
if err != nil {
return nil, err
}
return &worker{
Conn: conn,
- runner: req,
iterCount: iterCount,
lat: make([]time.Duration, 0, 100000),
}, nil
}
func (w *worker) Run(_ context.Context) error {
+ count := atomic.AddInt64(w.iterCount, -1)
+ if count < 0 {
+ return pool.Done
+ }
lat, lon := randPoint()
start := time.Now()
- if err := w.runner(w.Conn, lat, lon); err != nil {
+ if err := requestFuncs[count%3](w.Conn, lat, lon); err != nil {
return err
}
dur := time.Now().Sub(start)
w.lat = append(w.lat, dur)
- if atomic.AddInt64(w.iterCount, -1) < 0 {
- return pool.Done
- }
return nil
}
@@ -161,20 +166,11 @@
func (d durSlice) Less(i, j int) bool { return d[i] < d[j] }
func (d durSlice) Swap(i, j int) { d[i], d[j] = d[j], d[i] }
-type benchmark struct {
- sname string
- runner requestFunc
-}
-
-func (b *benchmark) name() string {
- return fmt.Sprintf("Tile38%sRequest", b.sname)
-}
-
-func (b *benchmark) run(d *driver.B, host string, port, clients int, iters int) error {
+func runBenchmark(d *driver.B, host string, port, clients int, iters int) error {
workers := make([]pool.Worker, 0, clients)
iterCount := int64(iters) // Shared atomic variable.
for i := 0; i < clients; i++ {
- w, err := newWorker(host, port, b.runner, &iterCount)
+ w, err := newWorker(host, port, &iterCount)
if err != nil {
return err
}
@@ -214,12 +210,6 @@
return nil
}
-var benchmarks = []benchmark{
- {"WithinCircle100km", doWithinCircle},
- {"IntersectsCircle100km", doIntersectsCircle},
- {"KNearestLimit100", doNearby},
-}
-
func launchServer(cfg *config, out io.Writer) (*exec.Cmd, error) {
// Set up arguments.
srvArgs := []string{
@@ -297,7 +287,9 @@
return n, driver.CopyDiagnosticData(cfg.diagnosticDataPath(diagnostics.Trace), diagnostics.Trace, benchName)
}
-func runOne(bench benchmark, cfg *config) (err error) {
+const benchName = "Tile38QueryLoad"
+
+func run(cfg *config) (err error) {
var buf bytes.Buffer
// Launch the server.
@@ -339,7 +331,7 @@
err = r
return
}
- if r := driver.WritePprofProfile(p, typ, bench.name()); r != nil {
+ if r := driver.WritePprofProfile(p, typ, benchName); r != nil {
err = r
return
}
@@ -357,11 +349,11 @@
driver.DoPerf(true),
driver.DoTrace(true),
}
- iters := 20 * 50000
+ iters := 40 * 50000
if cfg.short {
iters = 1000
}
- return driver.RunBenchmark(bench.name(), func(d *driver.B) error {
+ return driver.RunBenchmark(benchName, func(d *driver.B) error {
if driver.DiagnosticEnabled(diagnostics.Trace) {
// Handle execution tracing.
//
@@ -380,7 +372,7 @@
return
default:
}
- n, err := cfg.readTrace(bench.name())
+ n, err := cfg.readTrace(benchName)
if err != nil {
fmt.Fprintf(os.Stderr, "failed to read trace: %v", err)
return
@@ -395,7 +387,7 @@
d.Report("trace-bytes", traceBytes)
}()
}
- return bench.run(d, cfg.host, cfg.port, cfg.serverProcs, iters)
+ return runBenchmark(d, cfg.host, cfg.port, cfg.serverProcs, iters)
}, opts...)
}
@@ -408,14 +400,8 @@
for _, typ := range diagnostics.Types() {
cliCfg.isProfiling = cliCfg.isProfiling || driver.DiagnosticEnabled(typ)
}
- benchmarks := benchmarks
- if cliCfg.short {
- benchmarks = benchmarks[:1]
- }
- for _, bench := range benchmarks {
- if err := runOne(bench, &cliCfg); err != nil {
- fmt.Fprintf(os.Stderr, "error: %v\n", err)
- os.Exit(1)
- }
+ if err := run(&cliCfg); err != nil {
+ fmt.Fprintf(os.Stderr, "error: %v\n", err)
+ os.Exit(1)
}
}