Skip to content

Commit 3b1c0ba

Browse files
aleks-psimonswine
andauthored
Reduce memory allocations when rendering diff flamegraphs (#4430)
* Reduce memory allocations when rendering diff flamegraphs * Add benchmark --------- Co-authored-by: Christian Simon <[email protected]>
1 parent aabcb26 commit 3b1c0ba

File tree

4 files changed

+68
-8
lines changed

4 files changed

+68
-8
lines changed

pkg/model/flamegraph_diff.go

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import (
44
"bytes"
55
"fmt"
66

7-
"github.com/grafana/pyroscope/pkg/og/structs/cappedarr"
7+
"github.com/grafana/pyroscope/pkg/util/minheap"
88

99
querierv1 "github.com/grafana/pyroscope/api/gen/proto/go/querier/v1"
1010
)
@@ -95,7 +95,8 @@ func NewFlamegraphDiff(left, right *Tree, maxNodes int64) (*querierv1.FlameGraph
9595
int64(i),
9696
}
9797

98-
res.Levels[level].Values = append(values, res.Levels[level].Values...)
98+
// We need to prepend values here, but this is expensive. We'll reverse the order later.
99+
res.Levels[level].Values = append(res.Levels[level].Values, values...)
99100
xLeftOffset += left.self
100101
xRghtOffset += rght.self
101102
otherLeftTotal, otherRghtTotal := int64(0), int64(0)
@@ -140,6 +141,11 @@ func NewFlamegraphDiff(left, right *Tree, maxNodes int64) (*querierv1.FlameGraph
140141
}
141142
}
142143

144+
// reverse each level's values since we appended values instead of prepending them
145+
for _, level := range res.Levels {
146+
reverseSliceInChunks(level.Values, 7)
147+
}
148+
143149
deltaEncoding(res.Levels, 0, 7)
144150
deltaEncoding(res.Levels, 3, 7)
145151

@@ -259,18 +265,29 @@ func combineMinValues(leftTree, rightTree *Tree, maxNodes int) uint64 {
259265
if maxNodes < 1 {
260266
return 0
261267
}
262-
// Trees are combined, meaning that their structures are
263-
// identical, therefore the resulting tree can not have
264-
// more nodes than any of them.
265268
treeSize := leftTree.size(make([]*node, 0, defaultDFSSize))
266269
if treeSize <= int64(maxNodes) {
267270
return 0
268271
}
269-
c := cappedarr.New(maxNodes)
272+
273+
h := make([]int64, 0, maxNodes)
270274
combineIterateWithTotal(leftTree, rightTree, func(left uint64, right uint64) bool {
271-
return c.Push(max(left, right))
275+
maxVal := int64(max(left, right))
276+
if len(h) >= maxNodes {
277+
if maxVal > h[0] {
278+
h = minheap.Pop(h)
279+
h = minheap.Push(h, maxVal)
280+
}
281+
} else {
282+
h = minheap.Push(h, maxVal)
283+
}
284+
return true
272285
})
273-
return c.MinValue()
286+
287+
if len(h) < maxNodes {
288+
return 0
289+
}
290+
return uint64(h[0])
274291
}
275292

276293
// iterate both trees, both trees must be returned from combineTree
@@ -364,3 +381,16 @@ func prependTreeNode(s []*node, x *node) []*node {
364381
s[0] = x
365382
return s
366383
}
384+
385+
// reverseSliceInChunks reverses a slice in chunks of the given size
386+
func reverseSliceInChunks(slice []int64, chunkSize int) {
387+
if len(slice) < chunkSize {
388+
return
389+
}
390+
391+
for i, j := 0, len(slice)-chunkSize; i < j; i, j = i+chunkSize, j-chunkSize {
392+
for k := 0; k < chunkSize; k++ {
393+
slice[i+k], slice[j+k] = slice[j+k], slice[i+k]
394+
}
395+
}
396+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package model
2+
3+
import (
4+
"os"
5+
"testing"
6+
7+
"github.com/stretchr/testify/require"
8+
)
9+
10+
func Benchmark_NewFlamegraphDiff(b *testing.B) {
11+
leftTreeBytes, err := os.ReadFile("testdata/diff_left_tree.bin")
12+
require.NoError(b, err)
13+
rightTreeBytes, err := os.ReadFile("testdata/diff_right_tree.bin")
14+
require.NoError(b, err)
15+
16+
leftTree, err := UnmarshalTree(leftTreeBytes)
17+
require.NoError(b, err)
18+
19+
rightTree, err := UnmarshalTree(rightTreeBytes)
20+
require.NoError(b, err)
21+
22+
b.ResetTimer()
23+
b.ReportAllocs()
24+
25+
for i := 0; i < b.N; i++ {
26+
diff, err := NewFlamegraphDiff(leftTree, rightTree, 163840)
27+
require.NoError(b, err)
28+
require.NotNil(b, diff)
29+
}
30+
}
3.98 MB
Binary file not shown.
3.91 MB
Binary file not shown.

0 commit comments

Comments
 (0)