mirror of
https://github.com/prometheus/prometheus
synced 2025-01-03 13:02:12 +00:00
[PERF] Labels: faster varint for dedupelabels
Including tests. Signed-off-by: Bryan Boreham <bjboreham@gmail.com>
This commit is contained in:
parent
c25d6d8ac6
commit
2ced2f6aec
@ -104,25 +104,27 @@ func (t *nameTable) ToName(num int) string {
|
|||||||
return t.byNum[num]
|
return t.byNum[num]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// "Varint" in this file is non-standard: we encode small numbers (up to 32767) in 2 bytes,
|
||||||
|
// because we expect most Prometheus to have more than 127 unique strings.
|
||||||
|
// And we don't encode numbers larger than 4 bytes because we don't expect more than 536,870,912 unique strings.
|
||||||
func decodeVarint(data string, index int) (int, int) {
|
func decodeVarint(data string, index int) (int, int) {
|
||||||
// Fast-path for common case of a single byte, value 0..127.
|
b := int(data[index]) + int(data[index+1])<<8
|
||||||
b := data[index]
|
index += 2
|
||||||
|
if b < 0x8000 {
|
||||||
|
return b, index
|
||||||
|
}
|
||||||
|
|
||||||
|
value := int(b & 0x7FFF)
|
||||||
|
b = int(data[index])
|
||||||
index++
|
index++
|
||||||
if b < 0x80 {
|
if b < 0x80 {
|
||||||
return int(b), index
|
return value | (b << 15), index
|
||||||
}
|
}
|
||||||
value := int(b & 0x7F)
|
|
||||||
for shift := uint(7); ; shift += 7 {
|
value |= (b & 0x7f) << 15
|
||||||
// Just panic if we go of the end of data, since all Labels strings are constructed internally and
|
b = int(data[index])
|
||||||
// malformed data indicates a bug, or memory corruption.
|
|
||||||
b := data[index]
|
|
||||||
index++
|
index++
|
||||||
value |= int(b&0x7F) << shift
|
return value | (b << 22), index
|
||||||
if b < 0x80 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return value, index
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func decodeString(t *nameTable, data string, index int) (string, int) {
|
func decodeString(t *nameTable, data string, index int) (string, int) {
|
||||||
@ -641,29 +643,24 @@ func marshalNumbersToSizedBuffer(nums []int, data []byte) int {
|
|||||||
|
|
||||||
func sizeVarint(x uint64) (n int) {
|
func sizeVarint(x uint64) (n int) {
|
||||||
// Most common case first
|
// Most common case first
|
||||||
if x < 1<<7 {
|
if x < 1<<15 {
|
||||||
return 1
|
return 2
|
||||||
}
|
}
|
||||||
if x >= 1<<56 {
|
if x < 1<<22 {
|
||||||
return 9
|
return 3
|
||||||
}
|
}
|
||||||
if x >= 1<<28 {
|
if x >= 1<<29 {
|
||||||
x >>= 28
|
panic("Number too large to represent")
|
||||||
n = 4
|
|
||||||
}
|
}
|
||||||
if x >= 1<<14 {
|
return 4
|
||||||
x >>= 14
|
|
||||||
n += 2
|
|
||||||
}
|
|
||||||
if x >= 1<<7 {
|
|
||||||
n++
|
|
||||||
}
|
|
||||||
return n + 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func encodeVarintSlow(data []byte, offset int, v uint64) int {
|
func encodeVarintSlow(data []byte, offset int, v uint64) int {
|
||||||
offset -= sizeVarint(v)
|
offset -= sizeVarint(v)
|
||||||
base := offset
|
base := offset
|
||||||
|
data[offset] = uint8(v)
|
||||||
|
v >>= 8
|
||||||
|
offset++
|
||||||
for v >= 1<<7 {
|
for v >= 1<<7 {
|
||||||
data[offset] = uint8(v&0x7f | 0x80)
|
data[offset] = uint8(v&0x7f | 0x80)
|
||||||
v >>= 7
|
v >>= 7
|
||||||
@ -673,11 +670,12 @@ func encodeVarintSlow(data []byte, offset int, v uint64) int {
|
|||||||
return base
|
return base
|
||||||
}
|
}
|
||||||
|
|
||||||
// Special code for the common case that a value is less than 128
|
// Special code for the common case that a value is less than 32768
|
||||||
func encodeVarint(data []byte, offset, v int) int {
|
func encodeVarint(data []byte, offset, v int) int {
|
||||||
if v < 1<<7 {
|
if v < 1<<15 {
|
||||||
offset--
|
offset -= 2
|
||||||
data[offset] = uint8(v)
|
data[offset] = uint8(v)
|
||||||
|
data[offset+1] = uint8(v >> 8)
|
||||||
return offset
|
return offset
|
||||||
}
|
}
|
||||||
return encodeVarintSlow(data, offset, uint64(v))
|
return encodeVarintSlow(data, offset, uint64(v))
|
||||||
|
50
model/labels/labels_dedupelabels_test.go
Normal file
50
model/labels/labels_dedupelabels_test.go
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
// Copyright 2024 The Prometheus Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
//go:build dedupelabels
|
||||||
|
|
||||||
|
package labels
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestVarint(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
v int
|
||||||
|
expected []byte
|
||||||
|
}{
|
||||||
|
{0, []byte{0, 0}},
|
||||||
|
{1, []byte{1, 0}},
|
||||||
|
{2, []byte{2, 0}},
|
||||||
|
{0x7FFF, []byte{0xFF, 0x7F}},
|
||||||
|
{0x8000, []byte{0x00, 0x80, 0x01}},
|
||||||
|
{0x8001, []byte{0x01, 0x80, 0x01}},
|
||||||
|
{0x3FFFFF, []byte{0xFF, 0xFF, 0x7F}},
|
||||||
|
{0x400000, []byte{0x00, 0x80, 0x80, 0x01}},
|
||||||
|
{0x400001, []byte{0x01, 0x80, 0x80, 0x01}},
|
||||||
|
{0x1FFFFFFF, []byte{0xFF, 0xFF, 0xFF, 0x7F}},
|
||||||
|
}
|
||||||
|
var buf [16]byte
|
||||||
|
for _, c := range cases {
|
||||||
|
n := encodeVarint(buf[:], len(buf), c.v)
|
||||||
|
require.Equal(t, len(c.expected), len(buf)-n)
|
||||||
|
require.Equal(t, c.expected, buf[n:])
|
||||||
|
got, m := decodeVarint(string(buf[:]), n)
|
||||||
|
require.Equal(t, c.v, got)
|
||||||
|
require.Equal(t, len(buf), m)
|
||||||
|
}
|
||||||
|
require.Panics(t, func() { encodeVarint(buf[:], len(buf), 1<<29) })
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user