Repository: VictoriaMetrics/VictoriaMetrics
Pull request #6432 (2024-06-07 09:22:30)

lib/promrelabel: speedup label match by `__name__`
The change adds a fastpath for `equalValue` comparisons against `__name__` label by avoiding calls to `toCanonicalLabelName` func. This speedups matches by metric name like `'foo'`. See bench stats below:
```
benchcmp old.txt new.txt

benchmark                                           old ns/op     new ns/op     delta
BenchmarkIfExpression/equal_label:_last-10          35.6          35.1          -1.18%
BenchmarkIfExpression/equal_label:_middle-10        18.3          17.3          -5.41%
BenchmarkIfExpression/equal_label:_first-10         1.20          1.24          +2.74%
BenchmarkIfExpression/equal___name__:_last-10       10.1          4.96          -50.75%
BenchmarkIfExpression/equal___name__:_middle-10     5.79          3.16          -45.41%
BenchmarkIfExpression/equal___name__:_first-10      1.17          1.05          -9.76%
```

### Describe Your Changes

Please provide a brief description of the changes you made. Be as specific as possible to help others understand the purpose and impact of your modifications.

### Checklist

The following checks are **mandatory**:

- [ ] My change adheres [VictoriaMetrics contributing guidelines](https://docs.victoriametrics.com/contributing/).



Patch:
diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md
index 257493fd7..e41464b3a 100644
--- a/docs/CHANGELOG.md
+++ b/docs/CHANGELOG.md
@@ -60,6 +60,7 @@ See also [LTS releases](https://docs.victoriametrics.com/lts-releases/).
 * FEATURE: expose metric `vm_indexdb_items_dropped_total` to track the number of IndexDB records that had to be dropped during ingestion. The reason of dropping the record will be annotated in `reason` label of the exposed metric. This change also comes with a new [alerting rule](https://github.com/VictoriaMetrics/VictoriaMetrics/blob/master/deployment/docker/alerts-health.yml) to track changes of this metric.
 * FEATURE: [alerts-health](https://github.com/VictoriaMetrics/VictoriaMetrics/blob/master/deployment/docker/alerts-health.yml): add new alerting rules `TooLongLabelValues` and `TooLongLabelNames` to notify about truncation of label values or names respectively. 
 * FEATURE: [stream aggregation](https://docs.victoriametrics.com/stream-aggregation/): expose `vm_streamaggr_ignored_samples_total` [counters](https://docs.victoriametrics.com/keyconcepts/#counter) at [`/metrics` page](https://docs.victoriametrics.com/#monitoring), which can be used for detecting amount of too old or NaN valued ignored samples. Expose also `vm_streamaggr_samples_lag_seconds` [histogram](https://docs.victoriametrics.com/keyconcepts/#histogram) to monitor aggregated samples lag.
+* FEATURE: [stream aggregation](https://docs.victoriametrics.com/stream-aggregation/): improve filtering speed of the received data samples if [match](https://docs.victoriametrics.com/stream-aggregation/#stream-aggregation-config) field is matching only [metric name](https://docs.victoriametrics.com/keyconcepts/#structure-of-a-metric).
 
 * BUGFIX: [vmui](https://docs.victoriametrics.com/#vmui): fix bug that prevents the first query trace from expanding on click event. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/6186). The issue was introduced in [v1.100.0](https://docs.victoriametrics.com/changelog/#v11000) release.
 * BUGFIX: [vmui](https://docs.victoriametrics.com/#vmui): fix calendar display when `UTC+00:00` timezone is set. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/6239).
diff --git a/lib/promrelabel/if_expression.go b/lib/promrelabel/if_expression.go
index 1f3506dfb..7d102a1f6 100644
--- a/lib/promrelabel/if_expression.go
+++ b/lib/promrelabel/if_expression.go
@@ -287,10 +287,22 @@ func (lf *labelFilter) match(labels []prompbmarshal.Label) bool {
 	return false
 }
 
+func (lf *labelFilter) equalNameValue(labels []prompbmarshal.Label) bool {
+	for _, label := range labels {
+		if label.Name == "__name__" {
+			return label.Value == lf.value
+		}
+	}
+	return false
+}
+
 func (lf *labelFilter) equalValue(labels []prompbmarshal.Label) bool {
+	if lf.label == "" {
+		return lf.equalNameValue(labels)
+	}
 	labelNameMatches := 0
 	for _, label := range labels {
-		if toCanonicalLabelName(label.Name) != lf.label {
+		if label.Name != lf.label {
 			continue
 		}
 		labelNameMatches++


Test patch:
diff --git a/lib/promrelabel/if_expression_timing_test.go b/lib/promrelabel/if_expression_timing_test.go
new file mode 100644
index 000000000..a407d8e86
--- /dev/null
+++ b/lib/promrelabel/if_expression_timing_test.go
@@ -0,0 +1,78 @@
+package promrelabel
+
+import (
+	"fmt"
+	"gopkg.in/yaml.v2"
+	"testing"
+
+	"github.com/VictoriaMetrics/VictoriaMetrics/lib/prompbmarshal"
+)
+
+func BenchmarkIfExpression(b *testing.B) {
+	const maxLabels = 100
+	labels := make([]prompbmarshal.Label, maxLabels)
+	for i := 0; i < maxLabels; i++ {
+		label := prompbmarshal.Label{
+			Name:  fmt.Sprintf("foo%d", i),
+			Value: fmt.Sprintf("bar%d", i),
+		}
+		labels[i] = label
+	}
+
+	b.Run("equal label: last", func(b *testing.B) {
+		n := maxLabels - 1
+		ifExpr := fmt.Sprintf(`'{foo%d="bar%d"}'`, n, n)
+		benchIfExpr(b, ifExpr, labels)
+	})
+	b.Run("equal label: middle", func(b *testing.B) {
+		n := maxLabels / 2
+		ifExpr := fmt.Sprintf(`'{foo%d="bar%d"}'`, n, n)
+		benchIfExpr(b, ifExpr, labels)
+	})
+	b.Run("equal label: first", func(b *testing.B) {
+		ifExpr := fmt.Sprintf(`'{foo%d="bar%d"}'`, 0, 0)
+		benchIfExpr(b, ifExpr, labels)
+	})
+
+	labels[maxLabels-1] = prompbmarshal.Label{
+		Name:  "__name__",
+		Value: "foo",
+	}
+	b.Run("equal __name__: last", func(b *testing.B) {
+		ifExpr := `foo`
+		benchIfExpr(b, ifExpr, labels)
+	})
+
+	labels[maxLabels/2] = prompbmarshal.Label{
+		Name:  "__name__",
+		Value: "foo",
+	}
+	b.Run("equal __name__: middle", func(b *testing.B) {
+		ifExpr := `foo`
+		benchIfExpr(b, ifExpr, labels)
+	})
+
+	labels[0] = prompbmarshal.Label{
+		Name:  "__name__",
+		Value: "foo",
+	}
+	b.Run("equal __name__: first", func(b *testing.B) {
+		ifExpr := `foo`
+		benchIfExpr(b, ifExpr, labels)
+	})
+}
+
+func benchIfExpr(b *testing.B, expr string, labels []prompbmarshal.Label) {
+	b.Helper()
+	var ie IfExpression
+	if err := yaml.UnmarshalStrict([]byte(expr), &ie); err != nil {
+		b.Fatalf("unexpected error during unmarshal: %s", err)
+	}
+	b.RunParallel(func(pb *testing.PB) {
+		for pb.Next() {
+			if !ie.Match(labels) {
+				panic(fmt.Sprintf("expected to have a match for %q", expr))
+			}
+		}
+	})
+}