Skip to content

Commit 2a24e9d

Browse files
committed
DO NOT SUBMIT: tool for parallel PGO testing on gomotes
For golang/go#55022. Change-Id: I3755d2178f8672ef9c14a97256b1cd86a4883ea8
1 parent 0829db3 commit 2a24e9d

File tree

1 file changed

+222
-0
lines changed

1 file changed

+222
-0
lines changed

cmd/pgosweep/main.go

Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
// Copyright 2022 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package main
6+
7+
import (
8+
"context"
9+
"fmt"
10+
"io"
11+
"log"
12+
"os"
13+
"os/exec"
14+
"strings"
15+
"sync"
16+
)
17+
18+
func Create(ctx context.Context, logger io.Writer, typ string) (string, error) {
19+
cmd := exec.CommandContext(ctx, "gomote", "create", typ)
20+
cmd.Stderr = logger
21+
22+
result, err := cmd.Output()
23+
if err != nil {
24+
return "", err
25+
}
26+
return strings.TrimSpace(string(result)), nil
27+
}
28+
29+
func Push(ctx context.Context, logger io.Writer, inst, goroot string) error {
30+
cmd := exec.CommandContext(ctx, "gomote", "push", inst)
31+
cmd.Stdout = logger
32+
cmd.Stderr = logger
33+
cmd.Env = append(cmd.Environ(), "GOROOT="+goroot)
34+
35+
fmt.Fprintf(logger, "Running %v\n", cmd.Args)
36+
if err := cmd.Run(); err != nil {
37+
return err
38+
}
39+
return nil
40+
}
41+
42+
func Destroy(ctx context.Context, logger io.Writer, inst string) error {
43+
cmd := exec.CommandContext(ctx, "gomote", "destroy", inst)
44+
cmd.Stdout = logger
45+
cmd.Stderr = logger
46+
47+
fmt.Fprintf(logger, "Running %v\n", cmd.Args)
48+
if err := cmd.Run(); err != nil {
49+
return err
50+
}
51+
return nil
52+
}
53+
54+
func Run(ctx context.Context, logger io.Writer, inst string, remoteCmd ...string) error {
55+
args := []string{"run"}
56+
args = append(args, inst)
57+
args = append(args, remoteCmd...)
58+
cmd := exec.CommandContext(ctx, "gomote", args...)
59+
cmd.Stdout = logger
60+
cmd.Stderr = logger
61+
62+
fmt.Fprintf(logger, "Running %v\n", cmd.Args)
63+
if err := cmd.Run(); err != nil {
64+
return err
65+
}
66+
return nil
67+
}
68+
69+
// gomote run times out after 2hr, so hack around that by using SSH.
70+
func RunViaSSH(ctx context.Context, logger io.Writer, inst string, remoteCmd string) error {
71+
cmd := exec.CommandContext(ctx, "gomote", "ssh", inst)
72+
cmd.Stderr = logger
73+
74+
result, err := cmd.Output()
75+
if err != nil {
76+
return fmt.Errorf("error running gomote ssh for ssh command: %w", err)
77+
}
78+
79+
// First line is SSH command. e.g., `$ /usr/bin/ssh -o ...`
80+
sshCLI := strings.SplitN(string(result), "\n", 2)[0]
81+
if len(sshCLI) < 2 {
82+
return fmt.Errorf("first line isn't command: %q", sshCLI)
83+
}
84+
sshCLI = sshCLI[2:]
85+
fmt.Fprintf(logger, "Got SSH command: %q\n", sshCLI)
86+
87+
args := strings.Fields(sshCLI)
88+
// ssh -t -t forces PTY allocation even though stdin isn't a PTY. This
89+
// is required because the coordinator SSH server doesn't support
90+
// running commands, it requires a PTY. So we fake it.
91+
args = append(args, "-t", "-t")
92+
93+
cmd = exec.CommandContext(ctx, args[0], args[1:]...)
94+
cmd.Stdout = logger
95+
cmd.Stderr = logger
96+
97+
stdin, err := cmd.StdinPipe()
98+
if err != nil {
99+
return fmt.Errorf("error creating stdin pipe: %w", err)
100+
}
101+
defer stdin.Close()
102+
103+
fmt.Fprintf(logger, "Running %v\n", cmd.Args)
104+
if err := cmd.Start(); err != nil {
105+
return fmt.Errorf("error starting ssh: %w", err)
106+
}
107+
108+
fmt.Fprintln(stdin, "TMPDIR=/workdir/tmp")
109+
fmt.Fprintln(stdin, "GOCACHE=/workdir/gocache")
110+
fmt.Fprintln(stdin, "export TMPDIR GOCACHE")
111+
fmt.Fprintln(stdin, remoteCmd)
112+
fmt.Fprintln(stdin, "exit") // since ssh thinks this is a PTY, explicitly ask the shell to exit.
113+
114+
return cmd.Wait()
115+
}
116+
117+
func run(cdfThresh float64, budget int) error {
118+
ctx := context.Background()
119+
120+
name := fmt.Sprintf("cdf%f-budget%d.log", cdfThresh, budget)
121+
f, err := os.Create(name)
122+
if err != nil {
123+
return fmt.Errorf("error creating log file: %w", err)
124+
}
125+
defer f.Close()
126+
logger := f
127+
128+
var inst string
129+
for {
130+
inst, err = Create(ctx, logger, "linux-amd64-perf")
131+
if err != nil {
132+
log.Printf("error creating instance: %v", err)
133+
continue
134+
}
135+
break
136+
}
137+
defer Destroy(ctx, logger, inst)
138+
139+
log.Printf("%s running on %s", name, inst)
140+
141+
if err := Run(ctx, logger, inst, "/bin/mount", "-o", "remount,size=30G", "/workdir"); err != nil {
142+
return fmt.Errorf("error remounting tmpfs on %s: %w", inst, err)
143+
}
144+
145+
goroot := "/usr/local/google/home/mpratt/src/go"
146+
if err := Push(ctx, logger, inst, goroot); err != nil {
147+
return fmt.Errorf("error pushing to %s: %w", inst, err)
148+
}
149+
150+
if err := Run(ctx, logger, inst, "./go/src/make.bash"); err != nil {
151+
return fmt.Errorf("error building Go on %s: %w", inst, err)
152+
}
153+
154+
// GOPATH must be in /workdir, otherwise the root partition will run out of space.
155+
if err := Run(ctx, logger, inst, "/workdir/go/bin/go", "env", "-w", "GOPATH=/workdir/gopath"); err != nil {
156+
return fmt.Errorf("error setting GOPATH on %s: %w", inst, err)
157+
}
158+
159+
if err := Run(ctx, logger, inst, "/bin/bash", "-c", "git clone https://go.googlesource.com/benchmarks /workdir/benchmarks && cd /workdir/benchmarks && /workdir/go/bin/go build -o /workdir/sweet golang.org/x/benchmarks/sweet/cmd/sweet"); err != nil {
160+
return fmt.Errorf("error building sweet on %s: %w", inst, err)
161+
}
162+
163+
if err := Run(ctx, logger, inst, "/bin/bash", "-c", "git clone https://go.googlesource.com/perf /workdir/perf && cd /workdir/perf && git fetch https://go.googlesource.com/perf refs/changes/69/309969/11 && git checkout FETCH_HEAD && /workdir/go/bin/go build -o /workdir/benchstat golang.org/x/perf/cmd/benchstat"); err != nil {
164+
return fmt.Errorf("error building benchstat on %s: %w", inst, err)
165+
}
166+
167+
if err := Run(ctx, logger, inst, "/bin/bash", "-c", fmt.Sprintf(`cat >/workdir/config.toml <<EOF
168+
[[config]]
169+
name = "experiment"
170+
goroot = "/workdir/go"
171+
envbuild = ["GOFLAGS=-gcflags=all=-d=inlinehotcallsitecdfthreshold=%f,inlinehotbudget=%d"]
172+
EOF`, cdfThresh, budget)); err != nil {
173+
return fmt.Errorf("error building writing config on %s: %w", inst, err)
174+
}
175+
176+
if err := Run(ctx, logger, inst, "/bin/bash", "-c", "/workdir/sweet get -assets-hash-file=/workdir/benchmarks/sweet/assets.hash -cache=/tmp/go-sweet-assets"); err != nil {
177+
return fmt.Errorf("error fetching sweet assests on %s: %w", inst, err)
178+
}
179+
180+
log.Printf("%s running sweet", name)
181+
if err := RunViaSSH(ctx, logger, inst, `/bin/bash -c "/workdir/sweet run -pgo -work-dir /workdir/work -results /workdir/results -cache /tmp/go-sweet-assets -bench-dir /workdir/benchmarks/sweet/benchmarks -count 10 -run all /workdir/config.toml"`); err != nil {
182+
return fmt.Errorf("error running sweet on %s: %w", inst, err)
183+
}
184+
185+
if err := Run(ctx, logger, inst, "/bin/bash", "-c", "cat /workdir/results/*/experiment.results > /workdir/nopgo.txt && cat /workdir/results/*/experiment.pgo.results > /workdir/pgo.txt"); err != nil {
186+
return fmt.Errorf("error grouping results on %s: %w", inst, err)
187+
}
188+
189+
if err := Run(ctx, logger, inst, "/bin/bash", "-c", "echo nopgo && cat /workdir/nopgo.txt && echo pgo && cat /workdir/pgo.txt"); err != nil {
190+
return fmt.Errorf("error dumping results on %s: %w", inst, err)
191+
}
192+
193+
if err := Run(ctx, logger, inst, "/workdir/benchstat", "/workdir/nopgo.txt", "/workdir/pgo.txt"); err != nil {
194+
return fmt.Errorf("error running benchstat on %s: %w", inst, err)
195+
}
196+
197+
return nil
198+
}
199+
200+
func main() {
201+
cdfThresh := []float64{90, 95, 99, 99.9}
202+
budget := []int{160, 1000, 2000, 4000}
203+
204+
var wg sync.WaitGroup
205+
for _, c := range cdfThresh {
206+
for _, b := range budget {
207+
c := c
208+
b := b
209+
210+
wg.Add(1)
211+
go func() {
212+
defer wg.Done()
213+
214+
if err := run(c, b); err != nil {
215+
log.Printf("error running c=%f, b=%d: %v", c, b, err)
216+
}
217+
}()
218+
}
219+
}
220+
221+
wg.Wait()
222+
}

0 commit comments

Comments
 (0)