Skip to content

Commit fba9246

Browse files
authored
fix: Use a pool of LabelPairs to reduce memory allocs (#2838)
1 parent d0e9c5d commit fba9246

File tree

2 files changed

+55
-20
lines changed

2 files changed

+55
-20
lines changed

pkg/phlaredb/block_querier.go

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1989,8 +1989,18 @@ func (b *singleBlockQuerier) Series(ctx context.Context, params *ingestv1.Series
19891989

19901990
func (b *singleBlockQuerier) getUniqueLabelsSets(postings index.Postings, names []string, fingerprints *map[uint64]struct{}) ([]*typesv1.Labels, error) {
19911991
var labelsSets []*typesv1.Labels
1992+
1993+
// This memory will be re-used between posting iterations to avoid
1994+
// re-allocating many *typesv1.LabelPair objects.
1995+
matchedLabelsPool := make(phlaremodel.Labels, len(names))
1996+
for i := range matchedLabelsPool {
1997+
matchedLabelsPool[i] = &typesv1.LabelPair{}
1998+
}
1999+
19922000
for postings.Next() {
1993-
matchedLabels := make(phlaremodel.Labels, 0, len(names))
2001+
// Reset the pool.
2002+
matchedLabelsPool = matchedLabelsPool[:0]
2003+
19942004
for _, name := range names {
19952005
value, err := b.index.LabelValueFor(postings.At(), name)
19962006
if err != nil {
@@ -1999,22 +2009,29 @@ func (b *singleBlockQuerier) getUniqueLabelsSets(postings index.Postings, names
19992009
}
20002010
return nil, err
20012011
}
2002-
matchedLabels = append(matchedLabels, &typesv1.LabelPair{
2003-
Name: name,
2004-
Value: value,
2005-
})
2012+
2013+
// Expand the pool's length and add this label to the end. The pool
2014+
// will always have enough capacity for all the labels.
2015+
matchedLabelsPool = matchedLabelsPool[:len(matchedLabelsPool)+1]
2016+
matchedLabelsPool[len(matchedLabelsPool)-1].Name = name
2017+
matchedLabelsPool[len(matchedLabelsPool)-1].Value = value
20062018
}
20072019

2008-
fp := matchedLabels.Hash()
2020+
fp := matchedLabelsPool.Hash()
20092021
_, ok := (*fingerprints)[fp]
20102022
if ok {
20112023
continue
20122024
}
20132025
(*fingerprints)[fp] = struct{}{}
20142026

2015-
labelsSets = append(labelsSets, &typesv1.Labels{
2016-
Labels: matchedLabels,
2017-
})
2027+
// Copy every element from the pool to a new slice.
2028+
labels := &typesv1.Labels{
2029+
Labels: make([]*typesv1.LabelPair, 0, len(matchedLabelsPool)),
2030+
}
2031+
for _, label := range matchedLabelsPool {
2032+
labels.Labels = append(labels.Labels, label.CloneVT())
2033+
}
2034+
labelsSets = append(labelsSets, labels)
20182035
}
20192036
return labelsSets, nil
20202037
}

pkg/phlaredb/block_querier_test.go

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,21 @@ func (f *fakeQuerier) SelectMatchingProfiles(ctx context.Context, params *ingest
230230
return iter.NewSliceIterator(profiles), nil
231231
}
232232

233+
func openSingleBlockQuerierIndex(t *testing.T, blockID string) *singleBlockQuerier {
234+
t.Helper()
235+
236+
reader, err := index.NewFileReader(fmt.Sprintf("testdata/%s/index.tsdb", blockID))
237+
require.NoError(t, err)
238+
239+
q := &singleBlockQuerier{
240+
metrics: newBlocksMetrics(nil),
241+
meta: &block.Meta{ULID: ulid.MustParse(blockID)},
242+
opened: true, // Skip trying to open the block.
243+
index: reader,
244+
}
245+
return q
246+
}
247+
233248
func TestSelectMatchingProfilesCleanUp(t *testing.T) {
234249
defer goleak.VerifyNone(t, goleak.IgnoreCurrent())
235250

@@ -245,15 +260,7 @@ func TestSelectMatchingProfilesCleanUp(t *testing.T) {
245260

246261
func Test_singleBlockQuerier_Series(t *testing.T) {
247262
ctx := context.Background()
248-
reader, err := index.NewFileReader("testdata/01HA2V3CPSZ9E0HMQNNHH89WSS/index.tsdb")
249-
assert.NoError(t, err)
250-
251-
q := &singleBlockQuerier{
252-
metrics: newBlocksMetrics(nil),
253-
meta: &block.Meta{ULID: ulid.MustParse("01HA2V3CPSZ9E0HMQNNHH89WSS")},
254-
opened: true, // Skip trying to open the block.
255-
index: reader,
256-
}
263+
q := openSingleBlockQuerierIndex(t, "01HA2V3CPSZ9E0HMQNNHH89WSS")
257264

258265
t.Run("get all names", func(t *testing.T) {
259266
want := []string{
@@ -1055,13 +1062,15 @@ func Test_singleBlockQuerier_ProfileTypes(t *testing.T) {
10551062
}
10561063

10571064
func Benchmark_singleBlockQuerier_Series(b *testing.B) {
1065+
const id = "01HA2V3CPSZ9E0HMQNNHH89WSS"
1066+
10581067
ctx := context.Background()
1059-
reader, err := index.NewFileReader("testdata/01HA2V3CPSZ9E0HMQNNHH89WSS/index.tsdb")
1068+
reader, err := index.NewFileReader(fmt.Sprintf("testdata/%s/index.tsdb", id))
10601069
assert.NoError(b, err)
10611070

10621071
q := &singleBlockQuerier{
10631072
metrics: newBlocksMetrics(nil),
1064-
meta: &block.Meta{ULID: ulid.MustParse("01HA2V3CPSZ9E0HMQNNHH89WSS")},
1073+
meta: &block.Meta{ULID: ulid.MustParse(id)},
10651074
opened: true, // Skip trying to open the block.
10661075
index: reader,
10671076
}
@@ -1083,6 +1092,15 @@ func Benchmark_singleBlockQuerier_Series(b *testing.B) {
10831092
})
10841093
}
10851094
})
1095+
1096+
b.Run("UI request", func(b *testing.B) {
1097+
for n := 0; n < b.N; n++ {
1098+
q.Series(ctx, &ingestv1.SeriesRequest{ //nolint:errcheck
1099+
Matchers: []string{},
1100+
LabelNames: []string{"pyroscope_app", "service_name", "__profile_type__", "__type__", "__name__"},
1101+
})
1102+
}
1103+
})
10861104
}
10871105

10881106
func Benchmark_singleBlockQuerier_LabelNames(b *testing.B) {

0 commit comments

Comments
 (0)