mirror of
https://github.com/prometheus/prometheus.git
synced 2025-03-05 20:59:13 -08:00
Reduce stringlabels memory usage for common labels
stringlabels stores all time series labels as a single string using this format: <length><name><length><value>[<length><name><length><value> ...] So a label set for my_metric{job=foo, instance="bar", env="prod", blank=""} would be encoded as: [8]__name__[9]my_metric[3]job[3]foo[8]instance[3]bar[3]env[4]prod[5]blank[0] This is a huge improvement over 'classic' labels implementation that stores all label names & values as seperate strings. There is some room for improvement though since some string are present more often than others. For example __name__ will be present for all label sets of every time series we store in HEAD, eating 1+8=9 bytes. Since __name__ is well known string we can try to use a single byte to store it in our encoded string, rather than repeat it in full each time. To be able to store strings that are short cut into a single byte we need to somehow signal that to the reader of the encoded string, for that we use the fact that zero length strings are rare and generaly not stored on time series. If we have an encoded string with zero length then this will now signal that it represents a mapped value - to learn the true value of this string we need to read the next byte which gives us index in a static mapping. That mapping must include empty string, so that we can still encode empty strings using this scheme. Example of our mapping (minimal version): 0: "" 1: "__name__" 2: "instance" 3: "job" With that mapping our example label set would be encoded as: [0]1[9]mymetric[0]3[3]foo[0]2[3]bar[3]env[4]prod[5]blank[0]0 The tricky bit is how to populate this mapping with useful strings that will result in measurable memory savings. This is further complicated by the fact that the mapping must remain static and cannot be modified during Prometheus lifetime. We can use all the 255 slots we have inside our mapping byte with well known generic strings and that will provide some measurable savings for all Prometheus users, and is essentially a slightly more compact stringlabels variant. We could also allow users to pass in a list of well know strings via flags, which will allow Prometheus operators to reduce memory usage for any labels if they know those are popular. Third option is to discover most popular strings from TSDB or WAL on startup, but that's more complicated and we might pick a list that would be the best set of mapped strings on startup, but after some time is no longer the best set. Benchmark results: goos: linux goarch: amd64 pkg: github.com/prometheus/prometheus/model/labels cpu: 13th Gen Intel(R) Core(TM) i7-13800H │ main.txt │ new1.txt │ │ sec/op │ sec/op vs base │ String-20 863.8n ± 4% 873.0n ± 4% ~ (p=0.353 n=10) Labels_Get/with_5_labels/first_label/get-20 4.763n ± 1% 5.035n ± 0% +5.72% (p=0.000 n=10) Labels_Get/with_5_labels/first_label/has-20 3.439n ± 0% 3.967n ± 0% +15.37% (p=0.000 n=10) Labels_Get/with_5_labels/middle_label/get-20 7.077n ± 1% 9.588n ± 1% +35.47% (p=0.000 n=10) Labels_Get/with_5_labels/middle_label/has-20 5.166n ± 0% 6.990n ± 1% +35.30% (p=0.000 n=10) Labels_Get/with_5_labels/last_label/get-20 9.181n ± 1% 12.970n ± 1% +41.26% (p=0.000 n=10) Labels_Get/with_5_labels/last_label/has-20 8.101n ± 1% 11.640n ± 1% +43.69% (p=0.000 n=10) Labels_Get/with_5_labels/not-found_label/get-20 3.974n ± 0% 4.768n ± 0% +19.98% (p=0.000 n=10) Labels_Get/with_5_labels/not-found_label/has-20 3.974n ± 0% 5.033n ± 0% +26.65% (p=0.000 n=10) Labels_Get/with_10_labels/first_label/get-20 4.761n ± 0% 5.042n ± 0% +5.90% (p=0.000 n=10) Labels_Get/with_10_labels/first_label/has-20 3.442n ± 0% 3.972n ± 0% +15.40% (p=0.000 n=10) Labels_Get/with_10_labels/middle_label/get-20 10.62n ± 1% 14.85n ± 1% +39.83% (p=0.000 n=10) Labels_Get/with_10_labels/middle_label/has-20 9.360n ± 1% 13.375n ± 0% +42.90% (p=0.000 n=10) Labels_Get/with_10_labels/last_label/get-20 18.19n ± 1% 22.00n ± 0% +20.97% (p=0.000 n=10) Labels_Get/with_10_labels/last_label/has-20 16.51n ± 0% 20.50n ± 1% +24.14% (p=0.000 n=10) Labels_Get/with_10_labels/not-found_label/get-20 3.985n ± 0% 4.768n ± 0% +19.62% (p=0.000 n=10) Labels_Get/with_10_labels/not-found_label/has-20 3.973n ± 0% 5.045n ± 0% +26.97% (p=0.000 n=10) Labels_Get/with_30_labels/first_label/get-20 4.773n ± 0% 5.050n ± 1% +5.80% (p=0.000 n=10) Labels_Get/with_30_labels/first_label/has-20 3.443n ± 1% 3.976n ± 2% +15.50% (p=0.000 n=10) Labels_Get/with_30_labels/middle_label/get-20 31.93n ± 0% 43.50n ± 1% +36.21% (p=0.000 n=10) Labels_Get/with_30_labels/middle_label/has-20 30.53n ± 0% 41.75n ± 1% +36.75% (p=0.000 n=10) Labels_Get/with_30_labels/last_label/get-20 106.55n ± 0% 71.17n ± 0% -33.21% (p=0.000 n=10) Labels_Get/with_30_labels/last_label/has-20 104.70n ± 0% 69.21n ± 1% -33.90% (p=0.000 n=10) Labels_Get/with_30_labels/not-found_label/get-20 3.976n ± 1% 4.772n ± 0% +20.03% (p=0.000 n=10) Labels_Get/with_30_labels/not-found_label/has-20 3.974n ± 0% 5.032n ± 0% +26.64% (p=0.000 n=10) Labels_Equals/equal-20 2.382n ± 0% 2.446n ± 0% +2.67% (p=0.000 n=10) Labels_Equals/not_equal-20 0.2741n ± 2% 0.2662n ± 2% -2.88% (p=0.001 n=10) Labels_Equals/different_sizes-20 0.2762n ± 3% 0.2652n ± 0% -3.95% (p=0.000 n=10) Labels_Equals/lots-20 2.381n ± 0% 2.386n ± 1% +0.23% (p=0.011 n=10) Labels_Equals/real_long_equal-20 6.087n ± 1% 5.558n ± 1% -8.70% (p=0.000 n=10) Labels_Equals/real_long_different_end-20 5.030n ± 0% 4.699n ± 0% -6.57% (p=0.000 n=10) Labels_Compare/equal-20 4.814n ± 1% 4.777n ± 0% -0.77% (p=0.000 n=10) Labels_Compare/not_equal-20 17.55n ± 8% 20.92n ± 1% +19.24% (p=0.000 n=10) Labels_Compare/different_sizes-20 3.711n ± 1% 3.707n ± 0% ~ (p=0.224 n=10) Labels_Compare/lots-20 27.09n ± 3% 28.73n ± 2% +6.05% (p=0.000 n=10) Labels_Compare/real_long_equal-20 27.91n ± 3% 15.67n ± 1% -43.86% (p=0.000 n=10) Labels_Compare/real_long_different_end-20 33.92n ± 1% 35.35n ± 1% +4.22% (p=0.000 n=10) Labels_Hash/typical_labels_under_1KB-20 59.63n ± 0% 59.67n ± 0% ~ (p=0.897 n=10) Labels_Hash/bigger_labels_over_1KB-20 73.42n ± 1% 73.81n ± 1% ~ (p=0.342 n=10) Labels_Hash/extremely_large_label_value_10MB-20 720.3µ ± 2% 715.2µ ± 3% ~ (p=0.971 n=10) Builder-20 371.6n ± 4% 1191.0n ± 3% +220.46% (p=0.000 n=10) Labels_Copy-20 85.52n ± 4% 53.90n ± 48% -36.97% (p=0.000 n=10) geomean 13.26n 14.68n +10.71% │ main.txt │ new1.txt │ │ B/op │ B/op vs base │ String-20 240.0 ± 0% 240.0 ± 0% ~ (p=1.000 n=10) ¹ Labels_Get/with_5_labels/first_label/get-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Get/with_5_labels/first_label/has-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Get/with_5_labels/middle_label/get-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Get/with_5_labels/middle_label/has-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Get/with_5_labels/last_label/get-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Get/with_5_labels/last_label/has-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Get/with_5_labels/not-found_label/get-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Get/with_5_labels/not-found_label/has-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Get/with_10_labels/first_label/get-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Get/with_10_labels/first_label/has-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Get/with_10_labels/middle_label/get-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Get/with_10_labels/middle_label/has-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Get/with_10_labels/last_label/get-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Get/with_10_labels/last_label/has-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Get/with_10_labels/not-found_label/get-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Get/with_10_labels/not-found_label/has-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Get/with_30_labels/first_label/get-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Get/with_30_labels/first_label/has-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Get/with_30_labels/middle_label/get-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Get/with_30_labels/middle_label/has-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Get/with_30_labels/last_label/get-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Get/with_30_labels/last_label/has-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Get/with_30_labels/not-found_label/get-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Get/with_30_labels/not-found_label/has-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Equals/equal-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Equals/not_equal-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Equals/different_sizes-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Equals/lots-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Equals/real_long_equal-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Equals/real_long_different_end-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Compare/equal-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Compare/not_equal-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Compare/different_sizes-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Compare/lots-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Compare/real_long_equal-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Compare/real_long_different_end-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Hash/typical_labels_under_1KB-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Hash/bigger_labels_over_1KB-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Hash/extremely_large_label_value_10MB-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Builder-20 224.0 ± 0% 192.0 ± 0% -14.29% (p=0.000 n=10) Labels_Copy-20 224.0 ± 0% 192.0 ± 0% -14.29% (p=0.000 n=10) geomean ² -0.73% ² ¹ all samples are equal ² summaries must be >0 to compute geomean │ main.txt │ new1.txt │ │ allocs/op │ allocs/op vs base │ String-20 1.000 ± 0% 1.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Get/with_5_labels/first_label/get-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Get/with_5_labels/first_label/has-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Get/with_5_labels/middle_label/get-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Get/with_5_labels/middle_label/has-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Get/with_5_labels/last_label/get-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Get/with_5_labels/last_label/has-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Get/with_5_labels/not-found_label/get-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Get/with_5_labels/not-found_label/has-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Get/with_10_labels/first_label/get-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Get/with_10_labels/first_label/has-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Get/with_10_labels/middle_label/get-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Get/with_10_labels/middle_label/has-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Get/with_10_labels/last_label/get-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Get/with_10_labels/last_label/has-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Get/with_10_labels/not-found_label/get-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Get/with_10_labels/not-found_label/has-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Get/with_30_labels/first_label/get-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Get/with_30_labels/first_label/has-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Get/with_30_labels/middle_label/get-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Get/with_30_labels/middle_label/has-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Get/with_30_labels/last_label/get-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Get/with_30_labels/last_label/has-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Get/with_30_labels/not-found_label/get-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Get/with_30_labels/not-found_label/has-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Equals/equal-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Equals/not_equal-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Equals/different_sizes-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Equals/lots-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Equals/real_long_equal-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Equals/real_long_different_end-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Compare/equal-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Compare/not_equal-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Compare/different_sizes-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Compare/lots-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Compare/real_long_equal-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Compare/real_long_different_end-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Hash/typical_labels_under_1KB-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Hash/bigger_labels_over_1KB-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Hash/extremely_large_label_value_10MB-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Builder-20 1.000 ± 0% 1.000 ± 0% ~ (p=1.000 n=10) ¹ Labels_Copy-20 1.000 ± 0% 1.000 ± 0% ~ (p=1.000 n=10) ¹ geomean ² +0.00% ² ¹ all samples are equal ² summaries must be >0 to compute geomean Signed-off-by: Lukasz Mierzwa <l.mierzwa@gmail.com>
This commit is contained in:
parent
e04913aea2
commit
565c6fa704
|
@ -23,6 +23,39 @@ import (
|
|||
"github.com/cespare/xxhash/v2"
|
||||
)
|
||||
|
||||
// List of labels that should be mapped to a single byte value.
|
||||
// Obviously can't have more than 256 here.
|
||||
var mappedLabels = []string{
|
||||
// Empty string, this must be present here.
|
||||
"",
|
||||
// These label names are always present on every time series.
|
||||
"__name__",
|
||||
"instance",
|
||||
"job",
|
||||
// Common label names.
|
||||
"code",
|
||||
"handler",
|
||||
"quantile",
|
||||
// Meta metric names injected by Prometheus itself.
|
||||
"scrape_body_size_bytes",
|
||||
"scrape_duration_seconds",
|
||||
"scrape_sample_limit",
|
||||
"scrape_samples_post_metric_relabeling",
|
||||
"scrape_samples_scraped",
|
||||
"scrape_series_added",
|
||||
"scrape_timeout_seconds",
|
||||
// Common metric names from client libraries.
|
||||
"process_cpu_seconds_total",
|
||||
"process_max_fds",
|
||||
"process_network_receive_bytes_total",
|
||||
"process_network_transmit_bytes_total",
|
||||
"process_open_fds",
|
||||
"process_resident_memory_bytes",
|
||||
"process_start_time_seconds ",
|
||||
"process_virtual_memory_bytes",
|
||||
"process_virtual_memory_max_bytes",
|
||||
}
|
||||
|
||||
// Labels is implemented by a single flat string holding name/value pairs.
|
||||
// Each name and value is preceded by its length in varint encoding.
|
||||
// Names are in order.
|
||||
|
@ -30,12 +63,15 @@ type Labels struct {
|
|||
data string
|
||||
}
|
||||
|
||||
func decodeSize(data string, index int) (int, int) {
|
||||
func decodeSize(data string, index int) (int, int, bool) {
|
||||
// Fast-path for common case of a single byte, value 0..127.
|
||||
b := data[index]
|
||||
index++
|
||||
if b == 0 {
|
||||
return 1, index, true
|
||||
}
|
||||
if b < 0x80 {
|
||||
return int(b), index
|
||||
return int(b), index, false
|
||||
}
|
||||
size := int(b & 0x7F)
|
||||
for shift := uint(7); ; shift += 7 {
|
||||
|
@ -48,15 +84,28 @@ func decodeSize(data string, index int) (int, int) {
|
|||
break
|
||||
}
|
||||
}
|
||||
return size, index
|
||||
return size, index, false
|
||||
}
|
||||
|
||||
func decodeString(data string, index int) (string, int) {
|
||||
var size int
|
||||
size, index = decodeSize(data, index)
|
||||
var mapped bool
|
||||
size, index, mapped = decodeSize(data, index)
|
||||
if mapped {
|
||||
b := data[index]
|
||||
return mappedLabels[int(b)], index + size
|
||||
}
|
||||
return data[index : index+size], index + size
|
||||
}
|
||||
|
||||
func encodeShortString(s string) (int, byte) {
|
||||
i := slices.Index(mappedLabels, s)
|
||||
if i >= 0 {
|
||||
return 0, byte(i)
|
||||
}
|
||||
return len(s), 0
|
||||
}
|
||||
|
||||
// Bytes returns ls as a byte slice.
|
||||
// It uses non-printing characters and so should not be used for printing.
|
||||
func (ls Labels) Bytes(buf []byte) []byte {
|
||||
|
@ -197,23 +246,37 @@ func (ls Labels) Get(name string) string {
|
|||
return "" // Prometheus does not store blank label names.
|
||||
}
|
||||
for i := 0; i < len(ls.data); {
|
||||
var size int
|
||||
size, i = decodeSize(ls.data, i)
|
||||
if ls.data[i] == name[0] {
|
||||
lName := ls.data[i : i+size]
|
||||
i += size
|
||||
var size, next int
|
||||
var mapped bool
|
||||
var lName, lValue string
|
||||
size, next, mapped = decodeSize(ls.data, i) // Read the key index and size.
|
||||
if mapped { // Key is a mapped string, so decode it fully and move i to the value index.
|
||||
lName, i = decodeString(ls.data, i)
|
||||
if lName == name {
|
||||
lValue, _ := decodeString(ls.data, i)
|
||||
lValue, _ = decodeString(ls.data, i)
|
||||
return lValue
|
||||
}
|
||||
} else {
|
||||
if ls.data[i] > name[0] { // Stop looking if we've gone past.
|
||||
if lName[0] > name[0] { // Stop looking if we've gone past.
|
||||
break
|
||||
}
|
||||
i += size
|
||||
} else { // Value is stored raw in the data string.
|
||||
i = next // Move index to the start of the key string.
|
||||
if ls.data[i] == name[0] {
|
||||
lName = ls.data[i : i+size]
|
||||
i += size // We got the key string, move the index to the start of the value.
|
||||
if lName == name {
|
||||
lValue, _ := decodeString(ls.data, i)
|
||||
return lValue
|
||||
}
|
||||
} else {
|
||||
if ls.data[i] > name[0] { // Stop looking if we've gone past.
|
||||
break
|
||||
}
|
||||
i += size
|
||||
}
|
||||
}
|
||||
size, i = decodeSize(ls.data, i)
|
||||
i += size
|
||||
size, i, _ = decodeSize(ls.data, i) // Read the value index and size.
|
||||
i += size // move the index past the value so we can read the next key.
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
@ -224,21 +287,33 @@ func (ls Labels) Has(name string) bool {
|
|||
return false // Prometheus does not store blank label names.
|
||||
}
|
||||
for i := 0; i < len(ls.data); {
|
||||
var size int
|
||||
size, i = decodeSize(ls.data, i)
|
||||
if ls.data[i] == name[0] {
|
||||
lName := ls.data[i : i+size]
|
||||
i += size
|
||||
var size, next int
|
||||
var mapped bool
|
||||
var lName string
|
||||
size, next, mapped = decodeSize(ls.data, i)
|
||||
if mapped {
|
||||
lName, i = decodeString(ls.data, i)
|
||||
if lName == name {
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
if ls.data[i] > name[0] { // Stop looking if we've gone past.
|
||||
if lName[0] > name[0] { // Stop looking if we've gone past.
|
||||
break
|
||||
}
|
||||
} else {
|
||||
i = next
|
||||
if ls.data[i] == name[0] {
|
||||
lName = ls.data[i : i+size]
|
||||
if lName == name {
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
if ls.data[i] > name[0] { // Stop looking if we've gone past.
|
||||
break
|
||||
}
|
||||
}
|
||||
i += size
|
||||
}
|
||||
size, i = decodeSize(ls.data, i)
|
||||
size, i, _ = decodeSize(ls.data, i)
|
||||
i += size
|
||||
}
|
||||
return false
|
||||
|
@ -356,10 +431,10 @@ func Compare(a, b Labels) int {
|
|||
// Now we know that there is some difference before the end of a and b.
|
||||
// Go back through the fields and find which field that difference is in.
|
||||
firstCharDifferent, i := i, 0
|
||||
size, nextI := decodeSize(a.data, i)
|
||||
size, nextI, _ := decodeSize(a.data, i)
|
||||
for nextI+size <= firstCharDifferent {
|
||||
i = nextI + size
|
||||
size, nextI = decodeSize(a.data, i)
|
||||
size, nextI, _ = decodeSize(a.data, i)
|
||||
}
|
||||
// Difference is inside this entry.
|
||||
aStr, _ := decodeString(a.data, i)
|
||||
|
@ -385,9 +460,9 @@ func (ls Labels) Len() int {
|
|||
count := 0
|
||||
for i := 0; i < len(ls.data); {
|
||||
var size int
|
||||
size, i = decodeSize(ls.data, i)
|
||||
size, i, _ = decodeSize(ls.data, i)
|
||||
i += size
|
||||
size, i = decodeSize(ls.data, i)
|
||||
size, i, _ = decodeSize(ls.data, i)
|
||||
i += size
|
||||
count++
|
||||
}
|
||||
|
@ -422,7 +497,7 @@ func (ls Labels) Validate(f func(l Label) error) error {
|
|||
func (ls Labels) DropMetricName() Labels {
|
||||
for i := 0; i < len(ls.data); {
|
||||
lName, i2 := decodeString(ls.data, i)
|
||||
size, i2 := decodeSize(ls.data, i2)
|
||||
size, i2, _ := decodeSize(ls.data, i2)
|
||||
i2 += size
|
||||
if lName == MetricName {
|
||||
if i == 0 { // Make common case fast with no allocations.
|
||||
|
@ -518,12 +593,27 @@ func marshalLabelsToSizedBuffer(lbls []Label, data []byte) int {
|
|||
|
||||
func marshalLabelToSizedBuffer(m *Label, data []byte) int {
|
||||
i := len(data)
|
||||
i -= len(m.Value)
|
||||
copy(data[i:], m.Value)
|
||||
i = encodeSize(data, i, len(m.Value))
|
||||
i -= len(m.Name)
|
||||
copy(data[i:], m.Name)
|
||||
i = encodeSize(data, i, len(m.Name))
|
||||
|
||||
size, b := encodeShortString(m.Value)
|
||||
if size == 0 {
|
||||
i--
|
||||
data[i] = b
|
||||
} else {
|
||||
i -= size
|
||||
copy(data[i:], m.Value)
|
||||
}
|
||||
i = encodeSize(data, i, size)
|
||||
|
||||
size, b = encodeShortString(m.Name)
|
||||
if size == 0 {
|
||||
i--
|
||||
data[i] = b
|
||||
} else {
|
||||
i -= size
|
||||
copy(data[i:], m.Name)
|
||||
}
|
||||
i = encodeSize(data, i, size)
|
||||
|
||||
return len(data) - i
|
||||
}
|
||||
|
||||
|
@ -581,9 +671,16 @@ func labelsSize(lbls []Label) (n int) {
|
|||
|
||||
func labelSize(m *Label) (n int) {
|
||||
// strings are encoded as length followed by contents.
|
||||
l := len(m.Name)
|
||||
l, _ := encodeShortString(m.Name)
|
||||
if l == 0 {
|
||||
l++
|
||||
}
|
||||
n += l + sizeVarint(uint64(l))
|
||||
l = len(m.Value)
|
||||
|
||||
l, _ = encodeShortString(m.Value)
|
||||
if l == 0 {
|
||||
l++
|
||||
}
|
||||
n += l + sizeVarint(uint64(l))
|
||||
return n
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue