diff options
| author | Raj Barik <rajbarik@uber.com> | 2022-09-09 11:29:32 -0700 |
|---|---|---|
| committer | Michael Pratt <mpratt@google.com> | 2022-10-28 14:23:26 +0000 |
| commit | 99862cd57dd9406180f238e8fa2fcc1d435fb163 (patch) | |
| tree | 237c8c84a594eff60d06c1a65039d3403d23cdea /src/cmd/compile/internal/test | |
| parent | 537c4354cb9fdf8812c0448bd8f8a3b9f9ab1736 (diff) | |
| download | go-99862cd57dd9406180f238e8fa2fcc1d435fb163.tar.xz | |
cmd/compile: Enables PGO in Go and performs profile-guided inlining
For #55022
Change-Id: I51f1ba166d5a66dcaf4b280756be4a6bf9545c5e
Reviewed-on: https://go-review.googlesource.com/c/go/+/429863
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Cherry Mui <cherryyz@google.com>
Reviewed-by: Michael Pratt <mpratt@google.com>
Run-TryBot: Cherry Mui <cherryyz@google.com>
Diffstat (limited to 'src/cmd/compile/internal/test')
| -rw-r--r-- | src/cmd/compile/internal/test/pgo_inl_test.go | 148 | ||||
| -rw-r--r-- | src/cmd/compile/internal/test/testdata/pgo/inline/inline_hot.go | 86 | ||||
| -rw-r--r-- | src/cmd/compile/internal/test/testdata/pgo/inline/inline_hot.pprof | bin | 0 -> 1423 bytes | |||
| -rw-r--r-- | src/cmd/compile/internal/test/testdata/pgo/inline/inline_hot_test.go | 47 |
4 files changed, 281 insertions, 0 deletions
diff --git a/src/cmd/compile/internal/test/pgo_inl_test.go b/src/cmd/compile/internal/test/pgo_inl_test.go new file mode 100644 index 0000000000..eeeeae933b --- /dev/null +++ b/src/cmd/compile/internal/test/pgo_inl_test.go @@ -0,0 +1,148 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package test + +import ( + "bufio" + "fmt" + "internal/testenv" + "io" + "io/ioutil" + "os" + "os/exec" + "regexp" + "strings" + "testing" +) + +// TestPGOIntendedInlining tests that specific functions are inlined. +func TestPGOIntendedInlining(t *testing.T) { + testenv.MustHaveGoRun(t) + t.Parallel() + + // Make a temporary directory to work in. + tmpdir, err := ioutil.TempDir("", "TestCode") + if err != nil { + t.Fatalf("Failed to create temporary directory: %v", err) + } + defer os.RemoveAll(tmpdir) + + want := map[string][]string{ + "cmd/compile/internal/test/testdata/pgo/inline": { + "(*BS).NS", + }, + } + + // The functions which are not expected to be inlined are as follows. + wantNot := map[string][]string{ + "cmd/compile/internal/test/testdata/pgo/inline": { + // The calling edge main->A is hot and the cost of A is large than + // inlineHotCalleeMaxBudget. + "A", + // The calling edge BenchmarkA" -> benchmarkB is cold + // and the cost of A is large than inlineMaxBudget. + "benchmarkB", + }, + } + + must := map[string]bool{ + "(*BS).NS": true, + } + + notInlinedReason := make(map[string]string) + pkgs := make([]string, 0, len(want)) + for pname, fnames := range want { + pkgs = append(pkgs, pname) + for _, fname := range fnames { + fullName := pname + "." + fname + if _, ok := notInlinedReason[fullName]; ok { + t.Errorf("duplicate func: %s", fullName) + } + notInlinedReason[fullName] = "unknown reason" + } + } + + // If the compiler emit "cannot inline for function A", the entry A + // in expectedNotInlinedList will be removed. + expectedNotInlinedList := make(map[string]struct{}) + for pname, fnames := range wantNot { + for _, fname := range fnames { + fullName := pname + "." + fname + expectedNotInlinedList[fullName] = struct{}{} + } + } + + // go test -bench=. -cpuprofile testdata/pgo/inline/inline_hot.pprof cmd/compile/internal/test/testdata/pgo/inline + curdir, err1 := os.Getwd() + if err1 != nil { + t.Fatal(err1) + } + gcflag_option := "-gcflags=-m -m -pgoprofile %s/testdata/pgo/inline/inline_hot.pprof" + gcflag := fmt.Sprintf(gcflag_option, curdir) + args := append([]string{"test", "-run=nope", gcflag}, pkgs...) + cmd := testenv.CleanCmdEnv(exec.Command(testenv.GoToolPath(t), args...)) + + pr, pw := io.Pipe() + cmd.Stdout = pw + cmd.Stderr = pw + cmdErr := make(chan error, 1) + go func() { + cmdErr <- cmd.Run() + pw.Close() + }() + scanner := bufio.NewScanner(pr) + curPkg := "" + canInline := regexp.MustCompile(`: can inline ([^ ]*)`) + haveInlined := regexp.MustCompile(`: inlining call to ([^ ]*)`) + cannotInline := regexp.MustCompile(`: cannot inline ([^ ]*): (.*)`) + for scanner.Scan() { + line := scanner.Text() + if strings.HasPrefix(line, "# ") { + curPkg = line[2:] + splits := strings.Split(curPkg, " ") + curPkg = splits[0] + continue + } + if m := haveInlined.FindStringSubmatch(line); m != nil { + fname := m[1] + delete(notInlinedReason, curPkg+"."+fname) + continue + } + if m := canInline.FindStringSubmatch(line); m != nil { + fname := m[1] + fullname := curPkg + "." + fname + // If function must be inlined somewhere, being inlinable is not enough + if _, ok := must[fullname]; !ok { + delete(notInlinedReason, fullname) + continue + } + } + if m := cannotInline.FindStringSubmatch(line); m != nil { + fname, reason := m[1], m[2] + fullName := curPkg + "." + fname + if _, ok := notInlinedReason[fullName]; ok { + // cmd/compile gave us a reason why + notInlinedReason[fullName] = reason + } + delete(expectedNotInlinedList, fullName) + continue + } + } + if err := <-cmdErr; err != nil { + t.Fatal(err) + } + if err := scanner.Err(); err != nil { + t.Fatal(err) + } + for fullName, reason := range notInlinedReason { + t.Errorf("%s was not inlined: %s", fullName, reason) + } + + // If the list expectedNotInlinedList is not empty, it indicates + // the functions in the expectedNotInlinedList are marked with caninline. + for fullName, _ := range expectedNotInlinedList { + t.Errorf("%s was expected not inlined", fullName) + } +} diff --git a/src/cmd/compile/internal/test/testdata/pgo/inline/inline_hot.go b/src/cmd/compile/internal/test/testdata/pgo/inline/inline_hot.go new file mode 100644 index 0000000000..c1d2a53983 --- /dev/null +++ b/src/cmd/compile/internal/test/testdata/pgo/inline/inline_hot.go @@ -0,0 +1,86 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// WARNING: Please avoid updating this file. If this file needs to be updated, then a new inline_hot.pprof file should be generated via "go test -bench=. -cpuprofile testdata/pgo/inline/inline_hot.pprof cmd/compile/internal/test/testdata/pgo/inline". +package main + +import ( + "time" +) + +type BS struct { + length uint + s []uint64 +} + +const wSize = uint(64) +const lWSize = uint(6) + +func D(i uint) int { + return int((i + (wSize - 1)) >> lWSize) +} + +func N(length uint) (bs *BS) { + bs = &BS{ + length, + make([]uint64, D(length)), + } + + return bs +} + +func (b *BS) S(i uint) *BS { + b.s[i>>lWSize] |= 1 << (i & (wSize - 1)) + return b +} + +var jn = [...]byte{ + 0, 1, 56, 2, 57, 49, 28, 3, 61, 58, 42, 50, 38, 29, 17, 4, + 62, 47, 59, 36, 45, 43, 51, 22, 53, 39, 33, 30, 24, 18, 12, 5, + 63, 55, 48, 27, 60, 41, 37, 16, 46, 35, 44, 21, 52, 32, 23, 11, + 54, 26, 40, 15, 34, 20, 31, 10, 25, 14, 19, 9, 13, 8, 7, 6, +} + +func T(v uint64) uint { + return uint(jn[((v&-v)*0x03f79d71b4ca8b09)>>58]) +} + +func (b *BS) NS(i uint) (uint, bool) { + x := int(i >> lWSize) + if x >= len(b.s) { + return 0, false + } + w := b.s[x] + w = w >> (i & (wSize - 1)) + if w != 0 { + return i + T(w), true + } + x = x + 1 + for x < len(b.s) { + if b.s[x] != 0 { + return uint(x)*wSize + T(b.s[x]), true + } + x = x + 1 + + } + return 0, false +} + +func A() { + s := N(100000) + for i := 0; i < 1000; i += 30 { + s.S(uint(i)) + } + for j := 0; j < 1000; j++ { + c := uint(0) + for i, e := s.NS(0); e; i, e = s.NS(i + 1) { + c++ + } + } +} + +func main() { + time.Sleep(time.Second) + A() +} diff --git a/src/cmd/compile/internal/test/testdata/pgo/inline/inline_hot.pprof b/src/cmd/compile/internal/test/testdata/pgo/inline/inline_hot.pprof Binary files differnew file mode 100644 index 0000000000..45ccb6132b --- /dev/null +++ b/src/cmd/compile/internal/test/testdata/pgo/inline/inline_hot.pprof diff --git a/src/cmd/compile/internal/test/testdata/pgo/inline/inline_hot_test.go b/src/cmd/compile/internal/test/testdata/pgo/inline/inline_hot_test.go new file mode 100644 index 0000000000..024d340785 --- /dev/null +++ b/src/cmd/compile/internal/test/testdata/pgo/inline/inline_hot_test.go @@ -0,0 +1,47 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// WARNING: Please avoid updating this file. If this file needs to be updated, then a new inline_hot.pprof file should be generated via "go test -bench=. -cpuprofile testdata/pgo/inline/inline_hot.pprof cmd/compile/internal/test/testdata/pgo/inline". +package main + +import "testing" + +func BenchmarkA(b *testing.B) { + benchmarkB(b) +} +func benchmarkB(b *testing.B) { + + for i := 0; true; { + A() + i = i + 1 + if i >= b.N { + break + } + A() + i = i + 1 + if i >= b.N { + break + } + A() + i = i + 1 + if i >= b.N { + break + } + A() + i = i + 1 + if i >= b.N { + break + } + A() + i = i + 1 + if i >= b.N { + break + } + A() + i = i + 1 + if i >= b.N { + break + } + } +} |
