github.com/shogo82148/std@v1.22.1-0.20240327122250-4e474527810c/cmd/compile/internal/pgo/irgraph.go (about)

     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  // A note on line numbers: when working with line numbers, we always use the
     6  // binary-visible relative line number. i.e., the line number as adjusted by
     7  // //line directives (ctxt.InnermostPos(ir.Node.Pos()).RelLine()). Use
     8  // NodeLineOffset to compute line offsets.
     9  //
    10  // If you are thinking, "wait, doesn't that just make things more complex than
    11  // using the real line number?", then you are 100% correct. Unfortunately,
    12  // pprof profiles generated by the runtime always contain line numbers as
    13  // adjusted by //line directives (because that is what we put in pclntab). Thus
    14  // for the best behavior when attempting to match the source with the profile
    15  // it makes sense to use the same line number space.
    16  //
    17  // Some of the effects of this to keep in mind:
    18  //
    19  //  - For files without //line directives there is no impact, as RelLine() ==
    20  //    Line().
    21  //  - For functions entirely covered by the same //line directive (i.e., a
    22  //    directive before the function definition and no directives within the
    23  //    function), there should also be no impact, as line offsets within the
    24  //    function should be the same as the real line offsets.
    25  //  - Functions containing //line directives may be impacted. As fake line
    26  //    numbers need not be monotonic, we may compute negative line offsets. We
    27  //    should accept these and attempt to use them for best-effort matching, as
    28  //    these offsets should still match if the source is unchanged, and may
    29  //    continue to match with changed source depending on the impact of the
    30  //    changes on fake line numbers.
    31  //  - Functions containing //line directives may also contain duplicate lines,
    32  //    making it ambiguous which call the profile is referencing. This is a
    33  //    similar problem to multiple calls on a single real line, as we don't
    34  //    currently track column numbers.
    35  //
    36  // Long term it would be best to extend pprof profiles to include real line
    37  // numbers. Until then, we have to live with these complexities. Luckily,
    38  // //line directives that change line numbers in strange ways should be rare,
    39  // and failing PGO matching on these files is not too big of a loss.
    40  
    41  package pgo
    42  
    43  import (
    44  	"github.com/shogo82148/std/cmd/compile/internal/base"
    45  	"github.com/shogo82148/std/cmd/compile/internal/ir"
    46  )
    47  
    48  // IRGraph is a call graph with nodes pointing to IRs of functions and edges
    49  // carrying weights and callsite information.
    50  //
    51  // Nodes for indirect calls may have missing IR (IRNode.AST == nil) if the node
    52  // is not visible from this package (e.g., not in the transitive deps). Keeping
    53  // these nodes allows determining the hottest edge from a call even if that
    54  // callee is not available.
    55  //
    56  // TODO(prattmic): Consider merging this data structure with Graph. This is
    57  // effectively a copy of Graph aggregated to line number and pointing to IR.
    58  type IRGraph struct {
    59  	// Nodes of the graph. Each node represents a function, keyed by linker
    60  	// symbol name.
    61  	IRNodes map[string]*IRNode
    62  }
    63  
    64  // IRNode represents a node (function) in the IRGraph.
    65  type IRNode struct {
    66  	// Pointer to the IR of the Function represented by this node.
    67  	AST *ir.Func
    68  	// Linker symbol name of the Function represented by this node.
    69  	// Populated only if AST == nil.
    70  	LinkerSymbolName string
    71  
    72  	// Set of out-edges in the callgraph. The map uniquely identifies each
    73  	// edge based on the callsite and callee, for fast lookup.
    74  	OutEdges map[NamedCallEdge]*IREdge
    75  }
    76  
    77  // Name returns the symbol name of this function.
    78  func (i *IRNode) Name() string
    79  
    80  // IREdge represents a call edge in the IRGraph with source, destination,
    81  // weight, callsite, and line number information.
    82  type IREdge struct {
    83  	// Source and destination of the edge in IRNode.
    84  	Src, Dst       *IRNode
    85  	Weight         int64
    86  	CallSiteOffset int
    87  }
    88  
    89  // NamedCallEdge identifies a call edge by linker symbol names and call site
    90  // offset.
    91  type NamedCallEdge struct {
    92  	CallerName     string
    93  	CalleeName     string
    94  	CallSiteOffset int
    95  }
    96  
    97  // NamedEdgeMap contains all unique call edges in the profile and their
    98  // edge weight.
    99  type NamedEdgeMap struct {
   100  	Weight map[NamedCallEdge]int64
   101  
   102  	// ByWeight lists all keys in Weight, sorted by edge weight.
   103  	ByWeight []NamedCallEdge
   104  }
   105  
   106  // CallSiteInfo captures call-site information and its caller/callee.
   107  type CallSiteInfo struct {
   108  	LineOffset int
   109  	Caller     *ir.Func
   110  	Callee     *ir.Func
   111  }
   112  
   113  // Profile contains the processed PGO profile and weighted call graph used for
   114  // PGO optimizations.
   115  type Profile struct {
   116  	// Aggregated edge weights across the profile. This helps us determine
   117  	// the percentage threshold for hot/cold partitioning.
   118  	TotalWeight int64
   119  
   120  	// NamedEdgeMap contains all unique call edges in the profile and their
   121  	// edge weight.
   122  	NamedEdgeMap NamedEdgeMap
   123  
   124  	// WeightedCG represents the IRGraph built from profile, which we will
   125  	// update as part of inlining.
   126  	WeightedCG *IRGraph
   127  }
   128  
   129  // New generates a profile-graph from the profile or pre-processed profile.
   130  func New(profileFile string) (*Profile, error)
   131  
   132  // NodeLineOffset returns the line offset of n in fn.
   133  func NodeLineOffset(n ir.Node, fn *ir.Func) int
   134  
   135  // LookupFunc looks up a function or method in export data. It is expected to
   136  // be overridden by package noder, to break a dependency cycle.
   137  var LookupFunc = func(fullName string) (*ir.Func, error) {
   138  	base.Fatalf("pgo.LookupMethodFunc not overridden")
   139  	panic("unreachable")
   140  }
   141  
   142  // WeightInPercentage converts profile weights to a percentage.
   143  func WeightInPercentage(value int64, total int64) float64
   144  
   145  // PrintWeightedCallGraphDOT prints IRGraph in DOT format.
   146  func (p *Profile) PrintWeightedCallGraphDOT(edgeThreshold float64)
   147  
   148  // DirectCallee takes a function-typed expression and returns the underlying
   149  // function that it refers to if statically known. Otherwise, it returns nil.
   150  //
   151  // Equivalent to inline.inlCallee without calling CanInline on closures.
   152  func DirectCallee(fn ir.Node) *ir.Func