Replace sort.Sort with faster slices.SortFunc

The generic version is more efficient.

Signed-off-by: Bryan Boreham <bjboreham@gmail.com>
This commit is contained in:
Bryan Boreham 2023-07-08 12:45:56 +00:00
parent 26c354de0b
commit ce153e3fff
8 changed files with 40 additions and 81 deletions

View file

@ -17,6 +17,8 @@ import (
"math"
"sort"
"golang.org/x/exp/slices"
"github.com/prometheus/prometheus/model/histogram"
"github.com/prometheus/prometheus/model/labels"
)
@ -38,10 +40,6 @@ type bucket struct {
// buckets implements sort.Interface.
type buckets []bucket
func (b buckets) Len() int { return len(b) }
func (b buckets) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
func (b buckets) Less(i, j int) bool { return b[i].upperBound < b[j].upperBound }
type metricWithBuckets struct {
metric labels.Labels
buckets buckets
@ -83,7 +81,9 @@ func bucketQuantile(q float64, buckets buckets) float64 {
if q > 1 {
return math.Inf(+1)
}
sort.Sort(buckets)
slices.SortFunc(buckets, func(a, b bucket) bool {
return a.upperBound < b.upperBound
})
if !math.IsInf(buckets[len(buckets)-1].upperBound, +1) {
return math.NaN()
}

View file

@ -26,6 +26,7 @@ import (
"github.com/gogo/protobuf/proto"
"github.com/golang/snappy"
"github.com/prometheus/common/model"
"golang.org/x/exp/slices"
"github.com/prometheus/prometheus/model/exemplar"
"github.com/prometheus/prometheus/model/histogram"
@ -178,7 +179,9 @@ func FromQueryResult(sortSeries bool, res *prompb.QueryResult) storage.SeriesSet
}
if sortSeries {
sort.Sort(byLabel(series))
slices.SortFunc(series, func(a, b storage.Series) bool {
return labels.Compare(a.Labels(), b.Labels()) < 0
})
}
return &concreteSeriesSet{
series: series,
@ -313,12 +316,6 @@ func MergeLabels(primary, secondary []prompb.Label) []prompb.Label {
return result
}
type byLabel []storage.Series
func (a byLabel) Len() int { return len(a) }
func (a byLabel) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a byLabel) Less(i, j int) bool { return labels.Compare(a[i].Labels(), a[j].Labels()) < 0 }
// errSeriesSet implements storage.SeriesSet, just returning an error.
type errSeriesSet struct {
err error

View file

@ -450,7 +450,7 @@ func (s *memSeries) oooMergedChunk(meta chunks.Meta, cdm *chunks.ChunkDiskMapper
// Next we want to sort all the collected chunks by min time so we can find
// those that overlap and stop when we know the rest don't.
sort.Sort(byMinTimeAndMinRef(tmpChks))
slices.SortFunc(tmpChks, refLessByMinTimeAndMinRef)
mc := &mergedOOOChunks{}
absoluteMax := int64(math.MinInt64)

View file

@ -924,7 +924,7 @@ func (w *Writer) writePostingsToTmpFiles() error {
values = append(values, v)
}
// Symbol numbers are in order, so the strings will also be in order.
sort.Sort(uint32slice(values))
slices.Sort(values)
for _, v := range values {
value, err := w.symbols.Lookup(v)
if err != nil {
@ -1017,12 +1017,6 @@ func (w *Writer) writePostings() error {
return nil
}
type uint32slice []uint32
func (s uint32slice) Len() int { return len(s) }
func (s uint32slice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s uint32slice) Less(i, j int) bool { return s[i] < s[j] }
type labelIndexHashEntry struct {
keys []string
offset uint64

View file

@ -17,7 +17,8 @@ package tsdb
import (
"errors"
"math"
"sort"
"golang.org/x/exp/slices"
"github.com/prometheus/prometheus/model/labels"
"github.com/prometheus/prometheus/storage"
@ -130,7 +131,7 @@ func (oh *OOOHeadIndexReader) series(ref storage.SeriesRef, builder *labels.Scra
// Next we want to sort all the collected chunks by min time so we can find
// those that overlap.
sort.Sort(metaByMinTimeAndMinRef(tmpChks))
slices.SortFunc(tmpChks, lessByMinTimeAndMinRef)
// Next we want to iterate the sorted collected chunks and only return the
// chunks Meta the first chunk that overlaps with others.
@ -175,30 +176,20 @@ type chunkMetaAndChunkDiskMapperRef struct {
origMaxT int64
}
type byMinTimeAndMinRef []chunkMetaAndChunkDiskMapperRef
func (b byMinTimeAndMinRef) Len() int { return len(b) }
func (b byMinTimeAndMinRef) Less(i, j int) bool {
if b[i].meta.MinTime == b[j].meta.MinTime {
return b[i].meta.Ref < b[j].meta.Ref
func refLessByMinTimeAndMinRef(a, b chunkMetaAndChunkDiskMapperRef) bool {
if a.meta.MinTime == b.meta.MinTime {
return a.meta.Ref < b.meta.Ref
}
return b[i].meta.MinTime < b[j].meta.MinTime
return a.meta.MinTime < b.meta.MinTime
}
func (b byMinTimeAndMinRef) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
type metaByMinTimeAndMinRef []chunks.Meta
func (b metaByMinTimeAndMinRef) Len() int { return len(b) }
func (b metaByMinTimeAndMinRef) Less(i, j int) bool {
if b[i].MinTime == b[j].MinTime {
return b[i].Ref < b[j].Ref
func lessByMinTimeAndMinRef(a, b chunks.Meta) bool {
if a.MinTime == b.MinTime {
return a.Ref < b.Ref
}
return b[i].MinTime < b[j].MinTime
return a.MinTime < b.MinTime
}
func (b metaByMinTimeAndMinRef) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
func (oh *OOOHeadIndexReader) Postings(name string, values ...string) (index.Postings, error) {
switch len(values) {
case 0:

View file

@ -22,6 +22,7 @@ import (
"time"
"github.com/stretchr/testify/require"
"golang.org/x/exp/slices"
"github.com/prometheus/prometheus/model/labels"
"github.com/prometheus/prometheus/storage"
@ -337,7 +338,7 @@ func TestOOOHeadIndexReader_Series(t *testing.T) {
}
expChunks = append(expChunks, meta)
}
sort.Sort(metaByMinTimeAndMinRef(expChunks)) // we always want the chunks to come back sorted by minTime asc
slices.SortFunc(expChunks, lessByMinTimeAndMinRef) // We always want the chunks to come back sorted by minTime asc.
if headChunk && len(intervals) > 0 {
// Put the last interval in the head chunk
@ -1116,7 +1117,7 @@ func TestSortByMinTimeAndMinRef(t *testing.T) {
for _, tc := range tests {
t.Run(fmt.Sprintf("name=%s", tc.name), func(t *testing.T) {
sort.Sort(byMinTimeAndMinRef(tc.input))
slices.SortFunc(tc.input, refLessByMinTimeAndMinRef)
require.Equal(t, tc.exp, tc.input)
})
}
@ -1180,7 +1181,7 @@ func TestSortMetaByMinTimeAndMinRef(t *testing.T) {
for _, tc := range tests {
t.Run(fmt.Sprintf("name=%s", tc.name), func(t *testing.T) {
sort.Sort(metaByMinTimeAndMinRef(tc.inputMetas))
slices.SortFunc(tc.inputMetas, lessByMinTimeAndMinRef)
require.Equal(t, tc.expMetas, tc.inputMetas)
})
}

View file

@ -16,8 +16,9 @@ package stats
import (
"bytes"
"fmt"
"sort"
"time"
"golang.org/x/exp/slices"
)
// A Timer that can be started and stopped and accumulates the total time it
@ -78,35 +79,17 @@ func (t *TimerGroup) GetTimer(name fmt.Stringer) *Timer {
return timer
}
// Timers is a slice of Timer pointers that implements Len and Swap from
// sort.Interface.
type Timers []*Timer
type byCreationTimeSorter struct{ Timers }
// Len implements sort.Interface.
func (t Timers) Len() int {
return len(t)
}
// Swap implements sort.Interface.
func (t Timers) Swap(i, j int) {
t[i], t[j] = t[j], t[i]
}
func (s byCreationTimeSorter) Less(i, j int) bool {
return s.Timers[i].created < s.Timers[j].created
}
// Return a string representation of a TimerGroup.
func (t *TimerGroup) String() string {
timers := byCreationTimeSorter{}
timers := make([]*Timer, 0, len(t.timers))
for _, timer := range t.timers {
timers.Timers = append(timers.Timers, timer)
timers = append(timers, timer)
}
sort.Sort(timers)
slices.SortFunc(timers, func(a, b *Timer) bool {
return a.created < b.created
})
result := &bytes.Buffer{}
for _, timer := range timers.Timers {
for _, timer := range timers {
fmt.Fprintf(result, "%s\n", timer)
}
return result.String()

View file

@ -25,6 +25,7 @@ import (
dto "github.com/prometheus/client_model/go"
"github.com/prometheus/common/expfmt"
"github.com/prometheus/common/model"
"golang.org/x/exp/slices"
"github.com/prometheus/prometheus/model/histogram"
"github.com/prometheus/prometheus/model/labels"
@ -166,7 +167,11 @@ Loop:
return
}
sort.Sort(byName(vec))
slices.SortFunc(vec, func(a, b promql.Sample) bool {
ni := a.Metric.Get(labels.MetricName)
nj := b.Metric.Get(labels.MetricName)
return ni < nj
})
externalLabels := h.config.GlobalConfig.ExternalLabels.Map()
if _, ok := externalLabels[model.InstanceLabel]; !ok {
@ -313,15 +318,3 @@ Loop:
}
}
}
// byName makes a model.Vector sortable by metric name.
type byName promql.Vector
func (vec byName) Len() int { return len(vec) }
func (vec byName) Swap(i, j int) { vec[i], vec[j] = vec[j], vec[i] }
func (vec byName) Less(i, j int) bool {
ni := vec[i].Metric.Get(labels.MetricName)
nj := vec[j].Metric.Get(labels.MetricName)
return ni < nj
}