mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
cmd/compile/internal/inline: debug flag to alter score adjustments
Add a debugging flag "-d=inlscoreadj" intended to support running experiments in which the inliner uses different score adjustment values for specific heuristics. The flag argument is a series of clauses separated by the "/" char where each clause takes the form "adjK:valK". For example, in this build go build -gcflags=-d=inlscoreadj=inLoopAdj:10/returnFeedsConstToIfAdj:-99 the "in loop" score adjustments would be reset to a value of 15 (effectively penalizing calls in loops) adn the "return feeds constant to foldable if/switch" score adjustment would be boosted from -15 to -99. Change-Id: Ibd1ee334684af5992466556a69baa6dfefb246b3 Reviewed-on: https://go-review.googlesource.com/c/go/+/532116 Reviewed-by: Matthew Dempsky <mdempsky@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
This commit is contained in:
parent
6b43a172d8
commit
8d41d053e0
4 changed files with 145 additions and 1 deletions
|
|
@ -23,6 +23,8 @@ type DebugFlags struct {
|
||||||
DisableNil int `help:"disable nil checks" concurrent:"ok"`
|
DisableNil int `help:"disable nil checks" concurrent:"ok"`
|
||||||
DumpInlFuncProps string `help:"dump function properties from inl heuristics to specified file"`
|
DumpInlFuncProps string `help:"dump function properties from inl heuristics to specified file"`
|
||||||
DumpInlCallSiteScores int `help:"dump scored callsites during inlining"`
|
DumpInlCallSiteScores int `help:"dump scored callsites during inlining"`
|
||||||
|
InlScoreAdj string `help:"set inliner score adjustments (ex: -d=inlscoreadj=panicPathAdj:10/passConstToNestedIfAdj:-90)"`
|
||||||
|
InlBudgetSlack int `help:"amount to expand the initial inline budget when new inliner enabled. Defaults to 80 if option not set." concurrent:"ok"`
|
||||||
DumpPtrs int `help:"show Node pointers values in dump output"`
|
DumpPtrs int `help:"show Node pointers values in dump output"`
|
||||||
DwarfInl int `help:"print information about DWARF inlined function creation"`
|
DwarfInl int `help:"print information about DWARF inlined function creation"`
|
||||||
EscapeMutationsCalls int `help:"print extra escape analysis diagnostics about mutations and calls" concurrent:"ok"`
|
EscapeMutationsCalls int `help:"print extra escape analysis diagnostics about mutations and calls" concurrent:"ok"`
|
||||||
|
|
|
||||||
|
|
@ -141,6 +141,8 @@ func InlinePackage(p *pgo.Profile) {
|
||||||
p = nil
|
p = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inlheur.SetupScoreAdjustments()
|
||||||
|
|
||||||
InlineDecls(p, typecheck.Target.Funcs, true)
|
InlineDecls(p, typecheck.Target.Funcs, true)
|
||||||
|
|
||||||
// Perform a garbage collection of hidden closures functions that
|
// Perform a garbage collection of hidden closures functions that
|
||||||
|
|
@ -268,7 +270,7 @@ func inlineBudget(fn *ir.Func, profile *pgo.Profile, relaxed bool, verbose bool)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if relaxed {
|
if relaxed {
|
||||||
budget += inlineMaxBudget
|
budget += inlheur.BudgetExpansion(inlineMaxBudget)
|
||||||
}
|
}
|
||||||
return budget
|
return budget
|
||||||
}
|
}
|
||||||
|
|
|
||||||
65
src/cmd/compile/internal/inline/inlheur/debugflags_test.go
Normal file
65
src/cmd/compile/internal/inline/inlheur/debugflags_test.go
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
// Copyright 2023 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 inlheur
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestInlScoreAdjFlagParse(t *testing.T) {
|
||||||
|
scenarios := []struct {
|
||||||
|
value string
|
||||||
|
expok bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
value: "returnFeedsConcreteToInterfaceCallAdj:9",
|
||||||
|
expok: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "panicPathAdj:-1/initFuncAdj:9",
|
||||||
|
expok: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "",
|
||||||
|
expok: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "nonsenseAdj:10",
|
||||||
|
expok: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "inLoopAdj:",
|
||||||
|
expok: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "inLoopAdj:10:10",
|
||||||
|
expok: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "inLoopAdj:blah",
|
||||||
|
expok: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "/",
|
||||||
|
expok: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, scenario := range scenarios {
|
||||||
|
err := parseScoreAdj(scenario.value)
|
||||||
|
t.Logf("for value=%q err is %v\n", scenario.value, err)
|
||||||
|
if scenario.expok {
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("expected parseScoreAdj(%s) ok, got err %v",
|
||||||
|
scenario.value, err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("expected parseScoreAdj(%s) failure, got success",
|
||||||
|
scenario.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -13,6 +13,8 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"sort"
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// These constants enumerate the set of possible ways/scenarios
|
// These constants enumerate the set of possible ways/scenarios
|
||||||
|
|
@ -62,6 +64,8 @@ const (
|
||||||
returnFeedsFuncToIndCallAdj
|
returnFeedsFuncToIndCallAdj
|
||||||
returnFeedsInlinableFuncToIndCallAdj
|
returnFeedsInlinableFuncToIndCallAdj
|
||||||
returnFeedsConcreteToInterfaceCallAdj
|
returnFeedsConcreteToInterfaceCallAdj
|
||||||
|
|
||||||
|
sentinelScoreAdj // sentinel; not a real adjustment
|
||||||
)
|
)
|
||||||
|
|
||||||
// This table records the specific values we use to adjust call
|
// This table records the specific values we use to adjust call
|
||||||
|
|
@ -88,6 +92,56 @@ var adjValues = map[scoreAdjustTyp]int{
|
||||||
returnFeedsConcreteToInterfaceCallAdj: -25,
|
returnFeedsConcreteToInterfaceCallAdj: -25,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetupScoreAdjustments interprets the value of the -d=inlscoreadj
|
||||||
|
// debugging option, if set. The value of this flag is expected to be
|
||||||
|
// a series of "/"-separated clauses of the form adj1:value1. Example:
|
||||||
|
// -d=inlscoreadj=inLoopAdj=0/passConstToIfAdj=-99
|
||||||
|
func SetupScoreAdjustments() {
|
||||||
|
if base.Debug.InlScoreAdj == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := parseScoreAdj(base.Debug.InlScoreAdj); err != nil {
|
||||||
|
base.Fatalf("malformed -d=inlscoreadj argument %q: %v",
|
||||||
|
base.Debug.InlScoreAdj, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func adjStringToVal(s string) (scoreAdjustTyp, bool) {
|
||||||
|
for adj := scoreAdjustTyp(1); adj < sentinelScoreAdj; adj <<= 1 {
|
||||||
|
if adj.String() == s {
|
||||||
|
return adj, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseScoreAdj(val string) error {
|
||||||
|
clauses := strings.Split(val, "/")
|
||||||
|
if len(clauses) == 0 {
|
||||||
|
return fmt.Errorf("no clauses")
|
||||||
|
}
|
||||||
|
for _, clause := range clauses {
|
||||||
|
elems := strings.Split(clause, ":")
|
||||||
|
if len(elems) < 2 {
|
||||||
|
return fmt.Errorf("clause %q: expected colon", clause)
|
||||||
|
}
|
||||||
|
if len(elems) != 2 {
|
||||||
|
return fmt.Errorf("clause %q has %d elements, wanted 2", clause,
|
||||||
|
len(elems))
|
||||||
|
}
|
||||||
|
adj, ok := adjStringToVal(elems[0])
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("clause %q: unknown adjustment", clause)
|
||||||
|
}
|
||||||
|
val, err := strconv.Atoi(elems[1])
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("clause %q: malformed value: %v", clause, err)
|
||||||
|
}
|
||||||
|
adjValues[adj] = val
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func adjValue(x scoreAdjustTyp) int {
|
func adjValue(x scoreAdjustTyp) int {
|
||||||
if val, ok := adjValues[x]; ok {
|
if val, ok := adjValues[x]; ok {
|
||||||
return val
|
return val
|
||||||
|
|
@ -507,6 +561,27 @@ func GetCallSiteScore(fn *ir.Func, call *ir.CallExpr) (int, bool) {
|
||||||
return 0, false
|
return 0, false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BudgetExpansion returns the amount to relax/expand the base
|
||||||
|
// inlining budget when the new inliner is turned on; the inliner
|
||||||
|
// will add the returned value to the hairyness budget.
|
||||||
|
//
|
||||||
|
// Background: with the new inliner, the score for a given callsite
|
||||||
|
// can be adjusted down by some amount due to heuristics, however we
|
||||||
|
// won't know whether this is going to happen until much later after
|
||||||
|
// the CanInline call. This function returns the amount to relax the
|
||||||
|
// budget initially (to allow for a large score adjustment); later on
|
||||||
|
// in RevisitInlinability we'll look at each individual function to
|
||||||
|
// demote it if needed.
|
||||||
|
func BudgetExpansion(maxBudget int32) int32 {
|
||||||
|
if base.Debug.InlBudgetSlack != 0 {
|
||||||
|
return int32(base.Debug.InlBudgetSlack)
|
||||||
|
}
|
||||||
|
// In the default case, return maxBudget, which will effectively
|
||||||
|
// double the budget from 80 to 160; this should be good enough
|
||||||
|
// for most cases.
|
||||||
|
return maxBudget
|
||||||
|
}
|
||||||
|
|
||||||
var allCallSites CallSiteTab
|
var allCallSites CallSiteTab
|
||||||
|
|
||||||
// DumpInlCallSiteScores is invoked by the inliner if the debug flag
|
// DumpInlCallSiteScores is invoked by the inliner if the debug flag
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue