Skip to content

Commit

Permalink
Speed-up flame graph generation by skipping unneeded work.
Browse files Browse the repository at this point in the history
Previously, we used to call report.TextItems() during flame
graph generation just so we could get a hand on the legend
to print in the profile details box. All the work done by
TextItems() to produce a trimmed graph was discarded.

This change separates out the legend generation into a
separate routine so that we can avoid doing the unnecessary
work.

Benchmark result:

```
name      old time/op  new time/op  delta
Flame-12   6.10s ± 3%   0.39s ± 4%  -93.59%  (p=0.000 n=10+10)
```
  • Loading branch information
ghemawat committed Jul 16, 2024
1 parent f6c9dda commit f04b210
Show file tree
Hide file tree
Showing 3 changed files with 21 additions and 14 deletions.
4 changes: 1 addition & 3 deletions internal/driver/stacks.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import (
"net/http"

"github.com/google/pprof/internal/measurement"
"github.com/google/pprof/internal/report"
)

// stackView generates the flamegraph view.
Expand Down Expand Up @@ -51,8 +50,7 @@ func (ui *webInterface) stackView(w http.ResponseWriter, req *http.Request) {
}
nodes[0] = "" // root is not a real node

_, legend := report.TextItems(rpt)
ui.render(w, req, "stacks", rpt, errList, legend, webArgs{
ui.render(w, req, "stacks", rpt, errList, stacks.Legend(), webArgs{
Stacks: template.JS(b),
Nodes: nodes,
UnitDefs: measurement.UnitTypes,
Expand Down
24 changes: 13 additions & 11 deletions internal/report/report.go
Original file line number Diff line number Diff line change
Expand Up @@ -781,7 +781,7 @@ type TextItem struct {
func TextItems(rpt *Report) ([]TextItem, []string) {
g, origCount, droppedNodes, _ := rpt.newTrimmedGraph()
rpt.selectOutputUnit(g)
labels := reportLabels(rpt, g, origCount, droppedNodes, 0, false)
labels := reportLabels(rpt, graphTotal(g), len(g.Nodes), origCount, droppedNodes, 0, false)

var items []TextItem
var flatSum int64
Expand Down Expand Up @@ -1064,7 +1064,7 @@ func printTree(w io.Writer, rpt *Report) error {
g, origCount, droppedNodes, _ := rpt.newTrimmedGraph()
rpt.selectOutputUnit(g)

fmt.Fprintln(w, strings.Join(reportLabels(rpt, g, origCount, droppedNodes, 0, false), "\n"))
fmt.Fprintln(w, strings.Join(reportLabels(rpt, graphTotal(g), len(g.Nodes), origCount, droppedNodes, 0, false), "\n"))

fmt.Fprintln(w, separator)
fmt.Fprintln(w, legend)
Expand Down Expand Up @@ -1128,7 +1128,7 @@ func printTree(w io.Writer, rpt *Report) error {
func GetDOT(rpt *Report) (*graph.Graph, *graph.DotConfig) {
g, origCount, droppedNodes, droppedEdges := rpt.newTrimmedGraph()
rpt.selectOutputUnit(g)
labels := reportLabels(rpt, g, origCount, droppedNodes, droppedEdges, true)
labels := reportLabels(rpt, graphTotal(g), len(g.Nodes), origCount, droppedNodes, droppedEdges, true)

c := &graph.DotConfig{
Title: rpt.options.Title,
Expand Down Expand Up @@ -1184,12 +1184,19 @@ func ProfileLabels(rpt *Report) []string {
return label
}

func graphTotal(g *graph.Graph) int64 {
var total int64
for _, n := range g.Nodes {
total += n.FlatValue()
}
return total
}

// reportLabels returns printable labels for a report. Includes
// profileLabels.
func reportLabels(rpt *Report, g *graph.Graph, origCount, droppedNodes, droppedEdges int, fullHeaders bool) []string {
func reportLabels(rpt *Report, shownTotal int64, nodeCount, origCount, droppedNodes, droppedEdges int, fullHeaders bool) []string {
nodeFraction := rpt.options.NodeFraction
edgeFraction := rpt.options.EdgeFraction
nodeCount := len(g.Nodes)

var label []string
if len(rpt.options.ProfileLabels) > 0 {
Expand All @@ -1198,17 +1205,12 @@ func reportLabels(rpt *Report, g *graph.Graph, origCount, droppedNodes, droppedE
label = ProfileLabels(rpt)
}

var flatSum int64
for _, n := range g.Nodes {
flatSum = flatSum + n.FlatValue()
}

if len(rpt.options.ActiveFilters) > 0 {
activeFilters := legendActiveFilters(rpt.options.ActiveFilters)
label = append(label, activeFilters...)
}

label = append(label, fmt.Sprintf("Showing nodes accounting for %s, %s of %s total", rpt.formatValue(flatSum), strings.TrimSpace(measurement.Percentage(flatSum, rpt.total)), rpt.formatValue(rpt.total)))
label = append(label, fmt.Sprintf("Showing nodes accounting for %s, %s of %s total", rpt.formatValue(shownTotal), strings.TrimSpace(measurement.Percentage(shownTotal, rpt.total)), rpt.formatValue(rpt.total)))

if rpt.total != 0 {
if droppedNodes > 0 {
Expand Down
7 changes: 7 additions & 0 deletions internal/report/stacks.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ type StackSet struct {
Unit string // One of "B", "s", "GCU", or "" (if unknown)
Stacks []Stack // List of stored stacks
Sources []StackSource // Mapping from source index to info
report *Report
}

// Stack holds a single stack instance.
Expand Down Expand Up @@ -94,6 +95,7 @@ func (rpt *Report) Stacks() StackSet {
Unit: unit,
Stacks: []Stack{}, // Ensure non-nil
Sources: []StackSource{}, // Ensure non-nil
report: rpt,
}
s.makeInitialStacks(rpt)
s.fillPlaces()
Expand Down Expand Up @@ -187,3 +189,8 @@ func (s *StackSet) assignColors() {
s.Sources[i].Color = int(index % numColors)
}
}

// Legend returns the list of lines to display as the legend.
func (s *StackSet) Legend() []string {
return reportLabels(s.report, s.report.total, len(s.Sources), len(s.Sources), 0, 0, false)
}

0 comments on commit f04b210

Please sign in to comment.