perf(chunkenc): intern the custom values for native histograms

The custom values are the "le" bucket boundaries of native histograms
with custom buckets. They are never modified. It is ok to not copy them
when iterating a chunk, just reference them.

If we will ever have a function that modifies the custom values, like
'trim' for example. That function will have to make a copy on write.

Signed-off-by: György Krajcsovits <gyorgy.krajcsovits@grafana.com>
pull/16565/head
György Krajcsovits 2 months ago
parent 6dc6785473
commit 6c646657d5
No known key found for this signature in database
GPG Key ID: 47A8F9CE80FD7C7F
  1. 14
      model/histogram/float_histogram.go
  2. 23
      model/histogram/histogram.go
  3. 4
      tsdb/chunkenc/float_histogram.go
  4. 22
      tsdb/chunkenc/histogram.go
  5. 8
      tsdb/chunkenc/histogram_test.go

@ -73,10 +73,8 @@ func (h *FloatHistogram) Copy() *FloatHistogram {
} }
if h.UsesCustomBuckets() { if h.UsesCustomBuckets() {
if len(h.CustomValues) != 0 { // Custom values are interned, so no need to copy them.
c.CustomValues = make([]float64, len(h.CustomValues)) c.CustomValues = h.CustomValues
copy(c.CustomValues, h.CustomValues)
}
} else { } else {
c.ZeroThreshold = h.ZeroThreshold c.ZeroThreshold = h.ZeroThreshold
c.ZeroCount = h.ZeroCount c.ZeroCount = h.ZeroCount
@ -117,9 +115,8 @@ func (h *FloatHistogram) CopyTo(to *FloatHistogram) {
to.NegativeSpans = clearIfNotNil(to.NegativeSpans) to.NegativeSpans = clearIfNotNil(to.NegativeSpans)
to.NegativeBuckets = clearIfNotNil(to.NegativeBuckets) to.NegativeBuckets = clearIfNotNil(to.NegativeBuckets)
// Custom values are interned, so no need to copy them.
to.CustomValues = resize(to.CustomValues, len(h.CustomValues)) to.CustomValues = h.CustomValues
copy(to.CustomValues, h.CustomValues)
} else { } else {
to.ZeroThreshold = h.ZeroThreshold to.ZeroThreshold = h.ZeroThreshold
to.ZeroCount = h.ZeroCount to.ZeroCount = h.ZeroCount
@ -130,7 +127,8 @@ func (h *FloatHistogram) CopyTo(to *FloatHistogram) {
to.NegativeBuckets = resize(to.NegativeBuckets, len(h.NegativeBuckets)) to.NegativeBuckets = resize(to.NegativeBuckets, len(h.NegativeBuckets))
copy(to.NegativeBuckets, h.NegativeBuckets) copy(to.NegativeBuckets, h.NegativeBuckets)
to.CustomValues = clearIfNotNil(to.CustomValues) // Custom values are interned, so no need to reset them.
to.CustomValues = nil
} }
to.PositiveSpans = resize(to.PositiveSpans, len(h.PositiveSpans)) to.PositiveSpans = resize(to.PositiveSpans, len(h.PositiveSpans))

@ -102,10 +102,8 @@ func (h *Histogram) Copy() *Histogram {
} }
if h.UsesCustomBuckets() { if h.UsesCustomBuckets() {
if len(h.CustomValues) != 0 { // Custom values are interned, it's ok to copy by reference.
c.CustomValues = make([]float64, len(h.CustomValues)) c.CustomValues = h.CustomValues
copy(c.CustomValues, h.CustomValues)
}
} else { } else {
c.ZeroThreshold = h.ZeroThreshold c.ZeroThreshold = h.ZeroThreshold
c.ZeroCount = h.ZeroCount c.ZeroCount = h.ZeroCount
@ -146,9 +144,8 @@ func (h *Histogram) CopyTo(to *Histogram) {
to.NegativeSpans = clearIfNotNil(to.NegativeSpans) to.NegativeSpans = clearIfNotNil(to.NegativeSpans)
to.NegativeBuckets = clearIfNotNil(to.NegativeBuckets) to.NegativeBuckets = clearIfNotNil(to.NegativeBuckets)
// Custom values are interned, it's ok to copy by reference.
to.CustomValues = resize(to.CustomValues, len(h.CustomValues)) to.CustomValues = h.CustomValues
copy(to.CustomValues, h.CustomValues)
} else { } else {
to.ZeroThreshold = h.ZeroThreshold to.ZeroThreshold = h.ZeroThreshold
to.ZeroCount = h.ZeroCount to.ZeroCount = h.ZeroCount
@ -158,8 +155,8 @@ func (h *Histogram) CopyTo(to *Histogram) {
to.NegativeBuckets = resize(to.NegativeBuckets, len(h.NegativeBuckets)) to.NegativeBuckets = resize(to.NegativeBuckets, len(h.NegativeBuckets))
copy(to.NegativeBuckets, h.NegativeBuckets) copy(to.NegativeBuckets, h.NegativeBuckets)
// Custom values are interned, no need to reset.
to.CustomValues = clearIfNotNil(to.CustomValues) to.CustomValues = nil
} }
to.PositiveSpans = resize(to.PositiveSpans, len(h.PositiveSpans)) to.PositiveSpans = resize(to.PositiveSpans, len(h.PositiveSpans))
@ -379,9 +376,8 @@ func (h *Histogram) ToFloat(fh *FloatHistogram) *FloatHistogram {
fh.ZeroCount = 0 fh.ZeroCount = 0
fh.NegativeSpans = clearIfNotNil(fh.NegativeSpans) fh.NegativeSpans = clearIfNotNil(fh.NegativeSpans)
fh.NegativeBuckets = clearIfNotNil(fh.NegativeBuckets) fh.NegativeBuckets = clearIfNotNil(fh.NegativeBuckets)
// Custom values are interned, it's ok to copy by reference.
fh.CustomValues = resize(fh.CustomValues, len(h.CustomValues)) fh.CustomValues = h.CustomValues
copy(fh.CustomValues, h.CustomValues)
} else { } else {
fh.ZeroThreshold = h.ZeroThreshold fh.ZeroThreshold = h.ZeroThreshold
fh.ZeroCount = float64(h.ZeroCount) fh.ZeroCount = float64(h.ZeroCount)
@ -395,7 +391,8 @@ func (h *Histogram) ToFloat(fh *FloatHistogram) *FloatHistogram {
currentNegative += float64(b) currentNegative += float64(b)
fh.NegativeBuckets[i] = currentNegative fh.NegativeBuckets[i] = currentNegative
} }
fh.CustomValues = clearIfNotNil(fh.CustomValues) // Custom values are interned, no need to reset.
fh.CustomValues = nil
} }
fh.PositiveSpans = resize(fh.PositiveSpans, len(h.PositiveSpans)) fh.PositiveSpans = resize(fh.PositiveSpans, len(h.PositiveSpans))

@ -946,8 +946,8 @@ func (it *floatHistogramIterator) AtFloatHistogram(fh *histogram.FloatHistogram)
fh.NegativeBuckets = resize(fh.NegativeBuckets, len(it.nBuckets)) fh.NegativeBuckets = resize(fh.NegativeBuckets, len(it.nBuckets))
copy(fh.NegativeBuckets, it.nBuckets) copy(fh.NegativeBuckets, it.nBuckets)
fh.CustomValues = resize(fh.CustomValues, len(it.customValues)) // Custom values are interned. The single copy is in this iterator.
copy(fh.CustomValues, it.customValues) fh.CustomValues = it.customValues
return it.t, fh return it.t, fh
} }

@ -201,7 +201,8 @@ type HistogramAppender struct {
schema int32 schema int32
zThreshold float64 zThreshold float64
pSpans, nSpans []histogram.Span pSpans, nSpans []histogram.Span
customValues []float64 // customValues is read only after the first sample is appended.
customValues []float64
// Although we intend to start new chunks on counter resets, we still // Although we intend to start new chunks on counter resets, we still
// have to handle negative deltas for gauge histograms. Therefore, even // have to handle negative deltas for gauge histograms. Therefore, even
@ -995,8 +996,8 @@ func (it *histogramIterator) AtHistogram(h *histogram.Histogram) (int64, *histog
h.NegativeBuckets = resize(h.NegativeBuckets, len(it.nBuckets)) h.NegativeBuckets = resize(h.NegativeBuckets, len(it.nBuckets))
copy(h.NegativeBuckets, it.nBuckets) copy(h.NegativeBuckets, it.nBuckets)
h.CustomValues = resize(h.CustomValues, len(it.customValues)) // Custom values are interned. The single copy is here in the iterator.
copy(h.CustomValues, it.customValues) h.CustomValues = it.customValues
return it.t, h return it.t, h
} }
@ -1049,8 +1050,8 @@ func (it *histogramIterator) AtFloatHistogram(fh *histogram.FloatHistogram) (int
fh.NegativeBuckets[i] = currentNegative fh.NegativeBuckets[i] = currentNegative
} }
fh.CustomValues = resize(fh.CustomValues, len(it.customValues)) // Custom values are interned. The single copy is here in the iterator.
copy(fh.CustomValues, it.customValues) fh.CustomValues = it.customValues
return it.t, fh return it.t, fh
} }
@ -1081,7 +1082,6 @@ func (it *histogramIterator) Reset(b []byte) {
it.atHistogramCalled = false it.atHistogramCalled = false
it.pBuckets, it.nBuckets = nil, nil it.pBuckets, it.nBuckets = nil, nil
it.pSpans, it.nSpans = nil, nil it.pSpans, it.nSpans = nil, nil
it.customValues = nil
} else { } else {
it.pBuckets = it.pBuckets[:0] it.pBuckets = it.pBuckets[:0]
it.nBuckets = it.nBuckets[:0] it.nBuckets = it.nBuckets[:0]
@ -1089,7 +1089,6 @@ func (it *histogramIterator) Reset(b []byte) {
if it.atFloatHistogramCalled { if it.atFloatHistogramCalled {
it.atFloatHistogramCalled = false it.atFloatHistogramCalled = false
it.pFloatBuckets, it.nFloatBuckets = nil, nil it.pFloatBuckets, it.nFloatBuckets = nil, nil
it.customValues = nil
} else { } else {
it.pFloatBuckets = it.pFloatBuckets[:0] it.pFloatBuckets = it.pFloatBuckets[:0]
it.nFloatBuckets = it.nFloatBuckets[:0] it.nFloatBuckets = it.nFloatBuckets[:0]
@ -1102,6 +1101,7 @@ func (it *histogramIterator) Reset(b []byte) {
it.leading = 0 it.leading = 0
it.trailing = 0 it.trailing = 0
it.err = nil it.err = nil
it.customValues = nil
} }
func (it *histogramIterator) Next() ValueType { func (it *histogramIterator) Next() ValueType {
@ -1211,13 +1211,7 @@ func (it *histogramIterator) Next() ValueType {
} else { } else {
it.nSpans = nil it.nSpans = nil
} }
if len(it.customValues) > 0 { // it.CustomValues are interned, so we don't need to copy them.
newCustomValues := make([]float64, len(it.customValues))
copy(newCustomValues, it.customValues)
it.customValues = newCustomValues
} else {
it.customValues = nil
}
} }
if it.atHistogramCalled { if it.atHistogramCalled {

@ -1631,7 +1631,7 @@ func TestHistogramUniqueSpansAfterNextWithAtFloatHistogram(t *testing.T) {
require.NotSame(t, &rh1.NegativeSpans[0], &rh2.NegativeSpans[0], "NegativeSpans should be unique between histograms") require.NotSame(t, &rh1.NegativeSpans[0], &rh2.NegativeSpans[0], "NegativeSpans should be unique between histograms")
} }
func TestHistogramUniqueCustomValuesAfterNextWithAtHistogram(t *testing.T) { func TestHistogramCustomValuesInternedAfterNextWithAtHistogram(t *testing.T) {
// Create two histograms with the same schema and custom values. // Create two histograms with the same schema and custom values.
h1 := &histogram.Histogram{ h1 := &histogram.Histogram{
Schema: -53, Schema: -53,
@ -1674,10 +1674,10 @@ func TestHistogramUniqueCustomValuesAfterNextWithAtHistogram(t *testing.T) {
// Check that the spans and custom values for h1 and h2 are unique slices. // Check that the spans and custom values for h1 and h2 are unique slices.
require.NotSame(t, &rh1.PositiveSpans[0], &rh2.PositiveSpans[0], "PositiveSpans should be unique between histograms") require.NotSame(t, &rh1.PositiveSpans[0], &rh2.PositiveSpans[0], "PositiveSpans should be unique between histograms")
require.NotSame(t, &rh1.CustomValues[0], &rh2.CustomValues[0], "CustomValues should be unique between histograms") require.Same(t, &rh1.CustomValues[0], &rh2.CustomValues[0], "CustomValues should be unique between histograms")
} }
func TestHistogramUniqueCustomValuesAfterNextWithAtFloatHistogram(t *testing.T) { func TestHistogramCustomValuesInternedAfterNextWithAtFloatHistogram(t *testing.T) {
// Create two histograms with the same schema and custom values. // Create two histograms with the same schema and custom values.
h1 := &histogram.Histogram{ h1 := &histogram.Histogram{
Schema: -53, Schema: -53,
@ -1720,7 +1720,7 @@ func TestHistogramUniqueCustomValuesAfterNextWithAtFloatHistogram(t *testing.T)
// Check that the spans and custom values for h1 and h2 are unique slices. // Check that the spans and custom values for h1 and h2 are unique slices.
require.NotSame(t, &rh1.PositiveSpans[0], &rh2.PositiveSpans[0], "PositiveSpans should be unique between histograms") require.NotSame(t, &rh1.PositiveSpans[0], &rh2.PositiveSpans[0], "PositiveSpans should be unique between histograms")
require.NotSame(t, &rh1.CustomValues[0], &rh2.CustomValues[0], "CustomValues should be unique between histograms") require.Same(t, &rh1.CustomValues[0], &rh2.CustomValues[0], "CustomValues should be unique between histograms")
} }
func BenchmarkAppendable(b *testing.B) { func BenchmarkAppendable(b *testing.B) {

Loading…
Cancel
Save