From bb5746c3e4b12699a2ec4e16e63c00af59b4e6c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gy=C3=B6rgy=20Krajcsovits?= Date: Thu, 23 Jan 2025 13:49:31 +0100 Subject: [PATCH] perf(nhcb): reuse calculated metric name and hash if possible MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We advance the embedded parser one series at a time. Before we advance, we clear any calculated name and hash. During processing we remember if we already calculated the name or hash. Signed-off-by: György Krajcsovits --- model/textparse/nhcbparse.go | 53 ++++++++++++++++++++++++++++++------ 1 file changed, 45 insertions(+), 8 deletions(-) diff --git a/model/textparse/nhcbparse.go b/model/textparse/nhcbparse.go index 83e381539f..05034cce7e 100644 --- a/model/textparse/nhcbparse.go +++ b/model/textparse/nhcbparse.go @@ -36,6 +36,15 @@ const ( stateEmitting ) +// Keep track of what information we have already loaded about the next series. +type histogramInfoPreloaded struct { + hasName bool // The metricName and suffix is loaded. + hasHash bool // The hash was calculated. + metricName string // Without magic suffixes. + suffixType convertnhcb.SuffixType + hash uint64 +} + // The NHCBParser wraps a Parser and converts classic histograms to native // histograms with custom buckets. // @@ -59,6 +68,9 @@ type NHCBParser struct { // State of the parser. state collectionState + // Preloaded information about classic histogram series. + preload histogramInfoPreloaded + // Caches the values from the underlying parser. // For Series and Histogram. bytes []byte @@ -190,6 +202,11 @@ func (p *NHCBParser) Next() (Entry, error) { return p.entry, p.err } + // We are advancing the embedded parser, whatever we knew preloaded + // about the previous series is now invalid. + p.preload.hasName = false + p.preload.hasHash = false + p.entry, p.err = p.parser.Next() if p.err != nil { if errors.Is(p.err, io.EOF) && p.processNHCB() { @@ -237,6 +254,7 @@ func (p *NHCBParser) Next() (Entry, error) { } // Return true if labels have changed and we should emit the NHCB. +// Also store the calculated metric name and hash if possible for future use. func (p *NHCBParser) compareLabels() bool { if p.state != stateCollecting { return false @@ -245,26 +263,38 @@ func (p *NHCBParser) compareLabels() bool { // Different metric type. return true } - _, name := convertnhcb.GetHistogramMetricBaseName(p.lset.Get(labels.MetricName)) - if p.lastHistogramName != name { + p.preload.hasName = true + p.preload.suffixType, p.preload.metricName = convertnhcb.GetHistogramMetricBaseName(p.lset.Get(labels.MetricName)) + if p.lastHistogramName != p.preload.metricName { // Different metric name. return true } - nextHash, _ := p.lset.HashWithoutLabels(p.hBuffer, labels.BucketLabel) + p.preload.hasHash = true + p.preload.hash, _ = p.lset.HashWithoutLabels(p.hBuffer, labels.BucketLabel) // Different label values. - return p.lastHistogramLabelsHash != nextHash + return p.lastHistogramLabelsHash != p.preload.hash } // Save the label set of the classic histogram without suffix and bucket `le` label. func (p *NHCBParser) storeClassicLabels(name string) { p.lastHistogramName = name - p.lastHistogramLabelsHash, _ = p.lset.HashWithoutLabels(p.hBuffer, labels.BucketLabel) + if p.preload.hasHash { + p.lastHistogramLabelsHash = p.preload.hash + } else { + p.lastHistogramLabelsHash, _ = p.lset.HashWithoutLabels(p.hBuffer, labels.BucketLabel) + } p.lastHistogramExponential = false } func (p *NHCBParser) storeExponentialLabels() { p.lastHistogramName = p.lset.Get(labels.MetricName) - p.lastHistogramLabelsHash, _ = p.lset.HashWithoutLabels(p.hBuffer) + if p.preload.hasHash { + p.lastHistogramLabelsHash = p.preload.hash + } else { + // In the current model and client_golang, "le" label is not allowed + // for histogram so ignoring "le" here is ok. + p.lastHistogramLabelsHash, _ = p.lset.HashWithoutLabels(p.hBuffer, labels.BucketLabel) + } p.lastHistogramExponential = true } @@ -276,9 +306,16 @@ func (p *NHCBParser) handleClassicHistogramSeries(lset labels.Labels) bool { if p.typ != model.MetricTypeHistogram { return false } - mName := lset.Get(labels.MetricName) + + var name string + var suffixType convertnhcb.SuffixType + if p.preload.hasName { + suffixType, name = p.preload.suffixType, p.preload.metricName + } else { + suffixType, name = convertnhcb.GetHistogramMetricBaseName(lset.Get(labels.MetricName)) + } + // Sanity check to ensure that the TYPE metadata entry name is the same as the base name. - suffixType, name := convertnhcb.GetHistogramMetricBaseName(mName) if name != string(p.bName) { return false }