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:
Lukasz Mierzwa 2025-02-06 15:37:32 +00:00
parent e04913aea2
commit 565c6fa704

View file

@ -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
}