Skip to content

Commit 905a729

Browse files
committed
[PERF] Labels: faster varint for dedupelabels
Including tests. Signed-off-by: Bryan Boreham <[email protected]>
1 parent a2f6371 commit 905a729

File tree

2 files changed

+81
-33
lines changed

2 files changed

+81
-33
lines changed

model/labels/labels_dedupelabels.go

Lines changed: 31 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -105,25 +105,27 @@ func (t *nameTable) ToName(num int) string {
105105
return t.byNum[num]
106106
}
107107

108+
// "Varint" in this file is non-standard: we encode small numbers (up to 32767) in 2 bytes,
109+
// because we expect most Prometheus to have more than 127 unique strings.
110+
// And we don't encode numbers larger than 4 bytes because we don't expect more than 536,870,912 unique strings.
108111
func decodeVarint(data string, index int) (int, int) {
109-
// Fast-path for common case of a single byte, value 0..127.
110-
b := data[index]
112+
b := int(data[index]) + int(data[index+1])<<8
113+
index += 2
114+
if b < 0x8000 {
115+
return b, index
116+
}
117+
118+
value := int(b & 0x7FFF)
119+
b = int(data[index])
111120
index++
112121
if b < 0x80 {
113-
return int(b), index
114-
}
115-
value := int(b & 0x7F)
116-
for shift := uint(7); ; shift += 7 {
117-
// Just panic if we go of the end of data, since all Labels strings are constructed internally and
118-
// malformed data indicates a bug, or memory corruption.
119-
b := data[index]
120-
index++
121-
value |= int(b&0x7F) << shift
122-
if b < 0x80 {
123-
break
124-
}
122+
return value | (b << 15), index
125123
}
126-
return value, index
124+
125+
value |= (b & 0x7f) << 15
126+
b = int(data[index])
127+
index++
128+
return value | (b << 22), index
127129
}
128130

129131
func decodeString(t *nameTable, data string, index int) (string, int) {
@@ -646,29 +648,24 @@ func marshalNumbersToSizedBuffer(nums []int, data []byte) int {
646648

647649
func sizeVarint(x uint64) (n int) {
648650
// Most common case first
649-
if x < 1<<7 {
650-
return 1
651-
}
652-
if x >= 1<<56 {
653-
return 9
651+
if x < 1<<15 {
652+
return 2
654653
}
655-
if x >= 1<<28 {
656-
x >>= 28
657-
n = 4
654+
if x < 1<<22 {
655+
return 3
658656
}
659-
if x >= 1<<14 {
660-
x >>= 14
661-
n += 2
657+
if x >= 1<<29 {
658+
panic("Number too large to represent")
662659
}
663-
if x >= 1<<7 {
664-
n++
665-
}
666-
return n + 1
660+
return 4
667661
}
668662

669663
func encodeVarintSlow(data []byte, offset int, v uint64) int {
670664
offset -= sizeVarint(v)
671665
base := offset
666+
data[offset] = uint8(v)
667+
v >>= 8
668+
offset++
672669
for v >= 1<<7 {
673670
data[offset] = uint8(v&0x7f | 0x80)
674671
v >>= 7
@@ -678,11 +675,12 @@ func encodeVarintSlow(data []byte, offset int, v uint64) int {
678675
return base
679676
}
680677

681-
// Special code for the common case that a value is less than 128
678+
// Special code for the common case that a value is less than 32768
682679
func encodeVarint(data []byte, offset, v int) int {
683-
if v < 1<<7 {
684-
offset--
680+
if v < 1<<15 {
681+
offset -= 2
685682
data[offset] = uint8(v)
683+
data[offset+1] = uint8(v >> 8)
686684
return offset
687685
}
688686
return encodeVarintSlow(data, offset, uint64(v))
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// Copyright 2024 The Prometheus Authors
2+
// Licensed under the Apache License, Version 2.0 (the "License");
3+
// you may not use this file except in compliance with the License.
4+
// You may obtain a copy of the License at
5+
//
6+
// http://www.apache.org/licenses/LICENSE-2.0
7+
//
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
14+
//go:build dedupelabels
15+
16+
package labels
17+
18+
import (
19+
"testing"
20+
21+
"github.com/stretchr/testify/require"
22+
)
23+
24+
func TestVarint(t *testing.T) {
25+
cases := []struct {
26+
v int
27+
expected []byte
28+
}{
29+
{0, []byte{0, 0}},
30+
{1, []byte{1, 0}},
31+
{2, []byte{2, 0}},
32+
{0x7FFF, []byte{0xFF, 0x7F}},
33+
{0x8000, []byte{0x00, 0x80, 0x01}},
34+
{0x8001, []byte{0x01, 0x80, 0x01}},
35+
{0x3FFFFF, []byte{0xFF, 0xFF, 0x7F}},
36+
{0x400000, []byte{0x00, 0x80, 0x80, 0x01}},
37+
{0x400001, []byte{0x01, 0x80, 0x80, 0x01}},
38+
{0x1FFFFFFF, []byte{0xFF, 0xFF, 0xFF, 0x7F}},
39+
}
40+
var buf [16]byte
41+
for _, c := range cases {
42+
n := encodeVarint(buf[:], len(buf), c.v)
43+
require.Equal(t, len(c.expected), len(buf)-n)
44+
require.Equal(t, c.expected, buf[n:])
45+
got, m := decodeVarint(string(buf[:]), n)
46+
require.Equal(t, c.v, got)
47+
require.Equal(t, len(buf), m)
48+
}
49+
require.Panics(t, func() { encodeVarint(buf[:], len(buf), 1<<29) })
50+
}

0 commit comments

Comments
 (0)