From 2ab9a75ccaa191ca109f5f3d69c485f2f8a50ca5 Mon Sep 17 00:00:00 2001 From: Aliaksandr Valialkin Date: Mon, 22 Jan 2024 19:13:39 +0200 Subject: [PATCH] app/vmselect/netstorage: reduce the number of memory allocations in ProcessSearchQuery() by storing all the metric names in a single byte slice This reduces the number of memory allocations at the cost of possible memory usage increase, since now different metric name strings may hold references to the previous byte slice. This is good tradeoff, since ProcessSearchQuery is called in vmselect, and vmselect isn't usually limited by memory. This change has been extracted from https://github.com/VictoriaMetrics/VictoriaMetrics/pull/5527 --- app/vmselect/netstorage/netstorage.go | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/app/vmselect/netstorage/netstorage.go b/app/vmselect/netstorage/netstorage.go index eff53be7d8..35267b20d0 100644 --- a/app/vmselect/netstorage/netstorage.go +++ b/app/vmselect/netstorage/netstorage.go @@ -1351,6 +1351,11 @@ type tmpBlocksFileWrapper struct { tbfs []*tmpBlocksFile ms []map[string]*blockAddrs orderedMetricNamess [][]string + + // metricNamesBufs are per-worker bufs for holding all the loaded unique metric names. + // It should reduce pressure on Go garbage collector by reducing + // the number of memory allocations when constructing metricName string from byte slice. + metricNamesBufs [][]byte } type blockAddrs struct { @@ -1378,6 +1383,7 @@ func newTmpBlocksFileWrapper(sns []*storageNode) *tmpBlocksFileWrapper { tbfs: tbfs, ms: ms, orderedMetricNamess: make([][]string, n), + metricNamesBufs: make([][]byte, n), } } @@ -1389,8 +1395,6 @@ func (tbfw *tmpBlocksFileWrapper) RegisterAndWriteBlock(mb *storage.MetricBlock, if err != nil { return err } - // Do not intern mb.MetricName, since it leads to increased memory usage. - // See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3692 metricName := mb.MetricName m := tbfw.ms[workerID] addrs := m[string(metricName)] @@ -1399,13 +1403,17 @@ func (tbfw *tmpBlocksFileWrapper) RegisterAndWriteBlock(mb *storage.MetricBlock, } addrs.addrs = append(addrs.addrs, addr) if len(addrs.addrs) == 1 { - // An optimization for big number of time series with long names: store only a single copy of metricNameStr - // in both tbfw.orderedMetricNamess and tbfw.ms. + metricNamesBuf := tbfw.metricNamesBufs[workerID] + metricNamesBufLen := len(metricNamesBuf) + metricNamesBuf = append(metricNamesBuf, metricName...) + metricNameStr := bytesutil.ToUnsafeString(metricNamesBuf[metricNamesBufLen:]) + orderedMetricNames := tbfw.orderedMetricNamess[workerID] - metricNameStr := string(metricName) orderedMetricNames = append(orderedMetricNames, metricNameStr) m[metricNameStr] = addrs + tbfw.orderedMetricNamess[workerID] = orderedMetricNames + tbfw.metricNamesBufs[workerID] = metricNamesBuf } return nil }