From 94dbbdca7be5e4e6f73e802e8c5ad510e14e7446 Mon Sep 17 00:00:00 2001
From: Edd Robinson <me@edd.io>
Date: Tue, 13 Aug 2019 11:21:26 +0100
Subject: [PATCH] perf(storage): memoize hashmap prom labels
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Prior to this, each hashmap operation that was instrumented involved
initialising a map. Now these maps are pre-initialised.

```
⇒  benchstat old.txt new.txt
name                                                     old time/op    new time/op    delta
Index_CreateSeriesListIfNotExist/create_series-8            5.00s ± 3%     5.13s ± 2%   +2.50%  (p=0.033 n=10+7)
Index_CreateSeriesListIfNotExist/already_exist_series-8     557ms ± 3%     530ms ± 6%   -4.85%  (p=0.001 n=10+8)

name                                                     old alloc/op   new alloc/op   delta
Index_CreateSeriesListIfNotExist/create_series-8           2.57GB ± 0%    1.84GB ± 1%  -28.52%  (p=0.000 n=8+10)
Index_CreateSeriesListIfNotExist/already_exist_series-8     678MB ± 0%     308MB ± 0%  -54.55%  (p=0.000 n=10+8)

name                                                     old allocs/op  new allocs/op  delta
Index_CreateSeriesListIfNotExist/create_series-8            28.9M ± 0%     24.5M ± 0%  -15.22%  (p=0.000 n=9+10)
Index_CreateSeriesListIfNotExist/already_exist_series-8     2.23M ± 0%     0.03M ± 0%  -98.51%  (p=0.000 n=10+10)
```
---
 pkg/rhh/rhh.go | 64 +++++++++++++++++++++++++++++++-------------------
 1 file changed, 40 insertions(+), 24 deletions(-)

diff --git a/pkg/rhh/rhh.go b/pkg/rhh/rhh.go
index 3586b84092..2b50414c31 100644
--- a/pkg/rhh/rhh.go
+++ b/pkg/rhh/rhh.go
@@ -278,23 +278,41 @@ func (m *HashMap) PrometheusCollectors() []prometheus.Collector {
 }
 
 type rhhTracker struct {
-	metrics *Metrics
-	labels  prometheus.Labels
-	enabled bool
+	metrics    *Metrics
+	enabled    bool
+	baseLabels prometheus.Labels
+
+	// Prevent allocations by initialising these static maps when creating a
+	// new tracker.
+	hitIncLabels  prometheus.Labels
+	missIncLabels prometheus.Labels
 }
 
 // Labels returns a copy of the default labels used by the tracker's metrics.
 // The returned map is safe for modification.
 func (t *rhhTracker) Labels() prometheus.Labels {
-	labels := make(prometheus.Labels, len(t.labels))
-	for k, v := range t.labels {
+	labels := make(prometheus.Labels, len(t.baseLabels))
+	for k, v := range t.baseLabels {
 		labels[k] = v
 	}
 	return labels
 }
 
 func newRHHTracker(metrics *Metrics, defaultLabels prometheus.Labels) *rhhTracker {
-	return &rhhTracker{metrics: metrics, labels: defaultLabels, enabled: true}
+	tracker := &rhhTracker{metrics: metrics, enabled: true}
+
+	// Create a copy of the provided labels.
+	tracker.baseLabels = make(prometheus.Labels, len(defaultLabels))
+	for k, v := range defaultLabels {
+		tracker.baseLabels[k] = v
+	}
+
+	tracker.hitIncLabels = tracker.Labels()
+	tracker.hitIncLabels["status"] = "hit"
+	tracker.missIncLabels = tracker.Labels()
+	tracker.missIncLabels["status"] = "miss"
+
+	return tracker
 }
 
 func (t *rhhTracker) SetLoadFactor(load float64) {
@@ -302,8 +320,7 @@ func (t *rhhTracker) SetLoadFactor(load float64) {
 		return
 	}
 
-	labels := t.Labels()
-	t.metrics.LoadFactor.With(labels).Set(load)
+	t.metrics.LoadFactor.With(t.baseLabels).Set(load)
 }
 
 func (t *rhhTracker) SetSize(sz uint64) {
@@ -311,8 +328,7 @@ func (t *rhhTracker) SetSize(sz uint64) {
 		return
 	}
 
-	labels := t.Labels()
-	t.metrics.Size.With(labels).Set(float64(sz))
+	t.metrics.Size.With(t.baseLabels).Set(float64(sz))
 }
 
 func (t *rhhTracker) ObserveGet(d time.Duration) {
@@ -320,9 +336,8 @@ func (t *rhhTracker) ObserveGet(d time.Duration) {
 		return
 	}
 
-	labels := t.Labels()
-	t.metrics.GetDuration.With(labels).Observe(float64(d.Nanoseconds()))
-	t.metrics.LastGetDuration.With(labels).Set(float64(d.Nanoseconds()))
+	t.metrics.GetDuration.With(t.baseLabels).Observe(float64(d.Nanoseconds()))
+	t.metrics.LastGetDuration.With(t.baseLabels).Set(float64(d.Nanoseconds()))
 }
 
 func (t *rhhTracker) ObservePut(d time.Duration) {
@@ -330,9 +345,8 @@ func (t *rhhTracker) ObservePut(d time.Duration) {
 		return
 	}
 
-	labels := t.Labels()
-	t.metrics.InsertDuration.With(labels).Observe(float64(d.Nanoseconds()))
-	t.metrics.LastInsertDuration.With(labels).Set(float64(d.Nanoseconds()))
+	t.metrics.InsertDuration.With(t.baseLabels).Observe(float64(d.Nanoseconds()))
+	t.metrics.LastInsertDuration.With(t.baseLabels).Set(float64(d.Nanoseconds()))
 }
 
 func (t *rhhTracker) SetGrowDuration(d time.Duration) {
@@ -340,8 +354,7 @@ func (t *rhhTracker) SetGrowDuration(d time.Duration) {
 		return
 	}
 
-	labels := t.Labels()
-	t.metrics.LastGrowDuration.With(labels).Set(d.Seconds())
+	t.metrics.LastGrowDuration.With(t.baseLabels).Set(d.Seconds())
 }
 
 // TODO(edd): currently no safe way to calculate this concurrently.
@@ -350,8 +363,7 @@ func (t *rhhTracker) SetProbeCount(length float64) {
 		return
 	}
 
-	labels := t.Labels()
-	t.metrics.MeanProbeCount.With(labels).Set(length)
+	t.metrics.MeanProbeCount.With(t.baseLabels).Set(length)
 }
 
 func (t *rhhTracker) incGet(status string) {
@@ -359,8 +371,10 @@ func (t *rhhTracker) incGet(status string) {
 		return
 	}
 
-	labels := t.Labels()
-	labels["status"] = status
+	labels := t.hitIncLabels
+	if status == "miss" {
+		labels = t.missIncLabels
+	}
 	t.metrics.Gets.With(labels).Inc()
 }
 
@@ -372,8 +386,10 @@ func (t *rhhTracker) incPut(status string) {
 		return
 	}
 
-	labels := t.Labels()
-	labels["status"] = status
+	labels := t.hitIncLabels
+	if status == "miss" {
+		labels = t.missIncLabels
+	}
 	t.metrics.Puts.With(labels).Inc()
 }