Skip to content

Commit c4d21d0

Browse files
committed
store: Optimized labels conversion on store.Series; Added unsafe labels conversion.
## Changes * method TranslateLables CPU Optimized (streamed sorting). * All store GW label conversation to []storepb.Label are now alloc-less. ``` go test -bench=BenchmarkUnsafeVSSafeLabelsConversion -run=^$ -benchmem -timeout 2h -benchtime 10s ./pkg/store/storepb/... goos: linux goarch: amd64 pkg: github.com/thanos-io/thanos/pkg/store/storepb BenchmarkUnsafeVSSafeLabelsConversion/safe-12 34822 339076 ns/op 655368 B/op 2 allocs/op BenchmarkUnsafeVSSafeLabelsConversion/unsafe-12 1000000000 2.32 ns/op 0 B/op 0 allocs/op PASS ``` TODO: Do the same on Querier. Signed-off-by: Bartlomiej Plotka <[email protected]>
1 parent f332ece commit c4d21d0

20 files changed

+4260
-107
lines changed

go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ require (
5151
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
5252
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e
5353
golang.org/x/text v0.3.2
54-
golang.org/x/tools v0.0.0-20200221224223-e1da425f72fd // indirect
54+
golang.org/x/tools v0.0.0-20200306191617-51e69f71924f // indirect
5555
google.golang.org/api v0.14.0
5656
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9
5757
google.golang.org/grpc v1.25.1

go.sum

+7-4
Original file line numberDiff line numberDiff line change
@@ -771,8 +771,8 @@ golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU
771771
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
772772
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
773773
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
774-
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee h1:WG0RUwxtNT4qqaXX3DPA8zHFNm/D9xaBpxzHt1WcA/E=
775-
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
774+
golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ=
775+
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
776776
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
777777
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
778778
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -803,6 +803,8 @@ golang.org/x/net v0.0.0-20191002035440-2ec189313ef0/go.mod h1:z5CRVTTTmAJ677TzLL
803803
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
804804
golang.org/x/net v0.0.0-20191112182307-2180aed22343 h1:00ohfJ4K98s3m6BGUoBd8nyfp4Yl0GoIKvw5abItTjI=
805805
golang.org/x/net v0.0.0-20191112182307-2180aed22343/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
806+
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8=
807+
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
806808
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
807809
golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
808810
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -897,8 +899,9 @@ golang.org/x/tools v0.0.0-20191111182352-50fa39b762bc/go.mod h1:b+2E5dAYhXwXZwtn
897899
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
898900
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2 h1:EtTFh6h4SAKemS+CURDMTDIANuduG5zKEXShyy18bGA=
899901
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
900-
golang.org/x/tools v0.0.0-20200221224223-e1da425f72fd h1:hHkvGJK23seRCflePJnVa9IMv8fsuavSCWKd11kDQFs=
901-
golang.org/x/tools v0.0.0-20200221224223-e1da425f72fd/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
902+
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
903+
golang.org/x/tools v0.0.0-20200306191617-51e69f71924f h1:bFIWQKTZ5vXyr7xMDvzbWUj5Y/WBE4a4sf35MAyZjx0=
904+
golang.org/x/tools v0.0.0-20200306191617-51e69f71924f/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
902905
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
903906
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
904907
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=

indexheader.test

13.3 MB
Binary file not shown.

pkg/receive/handler.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@ import (
2323
"github.com/pkg/errors"
2424
"github.com/prometheus/client_golang/prometheus"
2525
"github.com/prometheus/common/route"
26-
"github.com/prometheus/prometheus/prompb"
2726
"github.com/prometheus/prometheus/storage"
2827
terrors "github.com/prometheus/prometheus/tsdb/errors"
28+
"github.com/thanos-io/thanos/pkg/store/storepb/prompb"
2929
"google.golang.org/grpc"
3030
"google.golang.org/grpc/codes"
3131
"google.golang.org/grpc/status"

pkg/receive/handler_test.go

+2-3
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,11 @@ import (
1919
"github.com/golang/snappy"
2020
"github.com/pkg/errors"
2121
"github.com/prometheus/prometheus/pkg/labels"
22-
"github.com/prometheus/prometheus/prompb"
2322
"github.com/prometheus/prometheus/storage"
2423
terrors "github.com/prometheus/prometheus/tsdb/errors"
25-
"google.golang.org/grpc"
26-
2724
"github.com/thanos-io/thanos/pkg/store/storepb"
25+
"github.com/thanos-io/thanos/pkg/store/storepb/prompb"
26+
"google.golang.org/grpc"
2827
)
2928

3029
func TestCountCause(t *testing.T) {

pkg/receive/hashring.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import (
1111

1212
"github.com/cespare/xxhash"
1313
"github.com/pkg/errors"
14-
"github.com/prometheus/prometheus/prompb"
14+
"github.com/thanos-io/thanos/pkg/store/storepb/prompb"
1515
)
1616

1717
const sep = '\xff'

pkg/receive/hashring_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ package receive
66
import (
77
"testing"
88

9-
"github.com/prometheus/prometheus/prompb"
9+
"github.com/thanos-io/thanos/pkg/store/storepb/prompb"
1010
)
1111

1212
func TestHash(t *testing.T) {

pkg/receive/writer.go

+1-2
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,10 @@ import (
99
"github.com/go-kit/kit/log"
1010
"github.com/go-kit/kit/log/level"
1111
"github.com/pkg/errors"
12-
"github.com/prometheus/prometheus/prompb"
13-
1412
"github.com/prometheus/prometheus/pkg/labels"
1513
"github.com/prometheus/prometheus/storage"
1614
terrors "github.com/prometheus/prometheus/tsdb/errors"
15+
"github.com/thanos-io/thanos/pkg/store/storepb/prompb"
1716
)
1817

1918
// Appendable returns an Appender.

pkg/store/prometheus.go

+24-27
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,13 @@ import (
2929
"github.com/prometheus/common/version"
3030
"github.com/prometheus/prometheus/pkg/labels"
3131
"github.com/prometheus/prometheus/pkg/timestamp"
32-
"github.com/prometheus/prometheus/prompb"
3332
"github.com/prometheus/prometheus/storage/remote"
3433
"github.com/prometheus/prometheus/tsdb/chunkenc"
3534
"github.com/thanos-io/thanos/pkg/component"
3635
"github.com/thanos-io/thanos/pkg/exthttp"
3736
"github.com/thanos-io/thanos/pkg/runutil"
3837
"github.com/thanos-io/thanos/pkg/store/storepb"
38+
"github.com/thanos-io/thanos/pkg/store/storepb/prompb"
3939
"github.com/thanos-io/thanos/pkg/tracing"
4040
"google.golang.org/grpc/codes"
4141
"google.golang.org/grpc/status"
@@ -178,12 +178,7 @@ func (p *PrometheusStore) Series(r *storepb.SeriesRequest, s storepb.Store_Serie
178178
for k, v := range lbm {
179179
lset = append(lset, storepb.Label{Name: k, Value: v})
180180
}
181-
for _, l := range externalLabels {
182-
lset = append(lset, storepb.Label{
183-
Name: l.Name,
184-
Value: l.Value,
185-
})
186-
}
181+
lset = append(lset, storepb.PromLabelsToLabelsUnsafe(externalLabels)...)
187182
sort.Slice(lset, func(i, j int) bool {
188183
return lset[i].Name < lset[j].Name
189184
})
@@ -518,32 +513,34 @@ func (p *PrometheusStore) encodeChunk(ss []prompb.Sample) (storepb.Chunk_Encodin
518513

519514
// translateAndExtendLabels transforms a metrics into a protobuf label set. It additionally
520515
// attaches the given labels to it, overwriting existing ones on collision.
516+
// Both input labels are expected to be sorted.
517+
//
518+
// NOTE(bwplotka): Don't use modify passed slices as we reuse underlying memory.
521519
func (p *PrometheusStore) translateAndExtendLabels(m []prompb.Label, extend labels.Labels) []storepb.Label {
520+
pbLabels := storepb.PrompbLabelsToLabelsUnsafe(m)
521+
pbExtend := storepb.PromLabelsToLabelsUnsafe(extend)
522+
522523
lset := make([]storepb.Label, 0, len(m)+len(extend))
524+
ei := 0
523525

524-
for _, l := range m {
525-
if extend.Get(l.Name) != "" {
526-
continue
526+
Outer:
527+
for _, l := range pbLabels {
528+
for ei < len(pbExtend) {
529+
if l.Name < pbExtend[ei].Name {
530+
break
531+
}
532+
lset = append(lset, pbExtend[ei])
533+
ei++
534+
if l.Name == pbExtend[ei-1].Name {
535+
continue Outer
536+
}
527537
}
528-
lset = append(lset, storepb.Label{
529-
Name: l.Name,
530-
Value: l.Value,
531-
})
538+
lset = append(lset, l)
532539
}
533-
534-
return extendLset(lset, extend)
535-
}
536-
537-
func extendLset(lset []storepb.Label, extend labels.Labels) []storepb.Label {
538-
for _, l := range extend {
539-
lset = append(lset, storepb.Label{
540-
Name: l.Name,
541-
Value: l.Value,
542-
})
540+
for ei < len(pbExtend) {
541+
lset = append(lset, pbExtend[ei])
542+
ei++
543543
}
544-
sort.Slice(lset, func(i, j int) bool {
545-
return lset[i].Name < lset[j].Name
546-
})
547544
return lset
548545
}
549546

pkg/store/prometheus_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,13 @@ import (
1414
"github.com/fortytw2/leaktest"
1515
"github.com/prometheus/prometheus/pkg/labels"
1616
"github.com/prometheus/prometheus/pkg/timestamp"
17-
"github.com/prometheus/prometheus/prompb"
1817
"github.com/prometheus/prometheus/tsdb"
1918
"github.com/prometheus/prometheus/tsdb/chunkenc"
2019
"github.com/thanos-io/thanos/pkg/testutil/e2eutil"
2120

2221
"github.com/thanos-io/thanos/pkg/component"
2322
"github.com/thanos-io/thanos/pkg/store/storepb"
23+
"github.com/thanos-io/thanos/pkg/store/storepb/prompb"
2424
"github.com/thanos-io/thanos/pkg/testutil"
2525
)
2626

pkg/store/storepb/custom.go

+44
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@ package storepb
55

66
import (
77
"strings"
8+
"unsafe"
89

910
"github.com/prometheus/prometheus/pkg/labels"
11+
"github.com/thanos-io/thanos/pkg/store/storepb/prompb"
1012
)
1113

1214
var PartialResponseStrategyValues = func() []string {
@@ -166,15 +168,57 @@ func (s *mergedSeriesSet) Next() bool {
166168
return true
167169
}
168170

171+
// LabelsToPromLabels converts Thanos proto labels to Prometheus labels in type safe manner.
169172
func LabelsToPromLabels(lset []Label) labels.Labels {
170173
ret := make(labels.Labels, len(lset))
171174
for i, l := range lset {
172175
ret[i] = labels.Label{Name: l.Name, Value: l.Value}
173176
}
177+
return ret
178+
}
179+
180+
// LabelsToPromLabelsUnsafe converts Thanos proto labels to Prometheus labels in type unsafe manner.
181+
// It reuses the same memory. Caller should abort using passed []Labels.
182+
//
183+
// NOTE: This depends on order of struct fields etc, so use with extreme care.
184+
func LabelsToPromLabelsUnsafe(lset []Label) labels.Labels {
185+
return *(*[]labels.Label)(unsafe.Pointer(&lset))
186+
}
174187

188+
// PromLabelsToLabels converts Prometheus labels to Thanos proto labels in type safe manner.
189+
func PromLabelsToLabels(lset labels.Labels) []Label {
190+
ret := make([]Label, len(lset))
191+
for i, l := range lset {
192+
ret[i] = Label{Name: l.Name, Value: l.Value}
193+
}
175194
return ret
176195
}
177196

197+
// PromLabelsToLabelsUnsafe converts Prometheus labels to Thanos proto labels in type unsafe manner.
198+
// It reuses the same memory. Caller should abort using passed labels.Labels.
199+
//
200+
// // NOTE: This depends on order of struct fields etc, so use with extreme care.
201+
func PromLabelsToLabelsUnsafe(lset labels.Labels) []Label {
202+
return *(*[]Label)(unsafe.Pointer(&lset))
203+
}
204+
205+
// PrompbLabelsToLabels converts Prometheus labels to Thanos proto labels in type safe manner.
206+
func PrompbLabelsToLabels(lset []prompb.Label) []Label {
207+
ret := make([]Label, len(lset))
208+
for i, l := range lset {
209+
ret[i] = Label{Name: l.Name, Value: l.Value}
210+
}
211+
return ret
212+
}
213+
214+
// PrompbLabelsToLabelsUnsafe converts Prometheus proto labels to Thanos proto labels in type unsafe manner.
215+
// It reuses the same memory. Caller should abort using passed labels.Labels.
216+
//
217+
// // NOTE: This depends on order of struct fields etc, so use with extreme care.
218+
func PrompbLabelsToLabelsUnsafe(lset []prompb.Label) []Label {
219+
return *(*[]Label)(unsafe.Pointer(&lset))
220+
}
221+
178222
func LabelsToString(lset []Label) string {
179223
var s []string
180224
for _, l := range lset {

pkg/store/storepb/custom_test.go

+56
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"github.com/pkg/errors"
1313
"github.com/prometheus/prometheus/pkg/labels"
1414
"github.com/prometheus/prometheus/tsdb/chunkenc"
15+
"github.com/thanos-io/thanos/pkg/store/storepb/prompb"
1516
"github.com/thanos-io/thanos/pkg/testutil"
1617
)
1718

@@ -328,3 +329,58 @@ func BenchmarkMergedSeriesSet(b *testing.B) {
328329
}
329330
}
330331
}
332+
333+
var testLsetMap = map[string]string{
334+
"a": "1",
335+
"c": "2",
336+
"d": "dsfsdfsdfsdf123414234",
337+
"124134235423534534ffdasdfsf": "1",
338+
"": "",
339+
"b": "",
340+
}
341+
342+
func TestPromLabelsToLabelsUnsafe(t *testing.T) {
343+
testutil.Equals(t, PromLabelsToLabels(labels.FromMap(testLsetMap)), PromLabelsToLabelsUnsafe(labels.FromMap(testLsetMap)))
344+
}
345+
346+
func TestLabelsToPromLabelsUnsafe(t *testing.T) {
347+
testutil.Equals(t, labels.FromMap(testLsetMap), LabelsToPromLabels(PromLabelsToLabels(labels.FromMap(testLsetMap))))
348+
testutil.Equals(t, labels.FromMap(testLsetMap), LabelsToPromLabelsUnsafe(PromLabelsToLabels(labels.FromMap(testLsetMap))))
349+
}
350+
351+
func TestPrompbLabelsToLabelsUnsafe(t *testing.T) {
352+
var pb []prompb.Label
353+
for _, l := range labels.FromMap(testLsetMap) {
354+
pb = append(pb, prompb.Label{Name: l.Name, Value: l.Value})
355+
}
356+
testutil.Equals(t, PromLabelsToLabels(labels.FromMap(testLsetMap)), PrompbLabelsToLabels(pb))
357+
testutil.Equals(t, PromLabelsToLabels(labels.FromMap(testLsetMap)), PrompbLabelsToLabelsUnsafe(pb))
358+
}
359+
360+
func BenchmarkUnsafeVSSafeLabelsConversion(b *testing.B) {
361+
const (
362+
fmtLbl = "%07daaaaaaaaaabbbbbbbbbbccccccccccdddddddddd"
363+
num = 10000
364+
)
365+
lbls := make([]labels.Label, 0, num)
366+
for i := 0; i < num; i++ {
367+
lbls = append(lbls, labels.Label{Name: fmt.Sprintf(fmtLbl, i), Value: fmt.Sprintf(fmtLbl, i)})
368+
}
369+
370+
var converted labels.Labels
371+
b.Run("safe", func(b *testing.B) {
372+
b.ResetTimer()
373+
for i := 0; i < b.N; i++ {
374+
converted = LabelsToPromLabels(PromLabelsToLabels(lbls))
375+
}
376+
})
377+
testutil.Equals(b, num, len(converted))
378+
b.Run("unsafe", func(b *testing.B) {
379+
b.ResetTimer()
380+
for i := 0; i < b.N; i++ {
381+
converted = LabelsToPromLabelsUnsafe(PromLabelsToLabelsUnsafe(lbls))
382+
}
383+
})
384+
testutil.Equals(b, num, len(converted))
385+
386+
}

pkg/store/storepb/prompb/README.md

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
NOTE(bwplotka): This excerpt of "github.com/prometheus/prometheus/prompb" reconstructed to avoid XXX fields for unsafe conversion to safe allocs.
2+
3+
The compiled protobufs are version controlled and you won't normally need to
4+
re-compile them when building Prometheus.
5+
6+
If however you have modified the defs and do need to re-compile, run
7+
`make proto` from the parent dir.
8+
9+
In order for the script to run, you'll need `protoc` (version 3.5.1) in your
10+
PATH.
11+

0 commit comments

Comments
 (0)