Skip to content

Commit

Permalink
Optimized.
Browse files Browse the repository at this point in the history
Signed-off-by: Bartlomiej Plotka <bwplotka@gmail.com>
  • Loading branch information
bwplotka committed Jan 21, 2022
1 parent 6d0a430 commit 84fe356
Show file tree
Hide file tree
Showing 7 changed files with 52 additions and 59 deletions.
63 changes: 26 additions & 37 deletions prometheus/cache/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ func (c *CachedTGatherer) Gather() (_ []*dto.MetricFamily, done func(), err erro
c.mMu.RLock()

// BenchmarkCachedTGatherer_Update shows, even for 1 million metrics with 1000 families
// this is efficient enough (~250µs 50 kB per op), no need to cache it for now.
// this is efficient enough (~300µs and ~50 kB per op), no need to cache it for now.
return internal.NormalizeMetricFamilies(c.metricFamilyByName), c.mMu.RUnlock, nil
}

Expand Down Expand Up @@ -114,6 +114,11 @@ type Insert struct {
// Update goes through inserts and deletions and updates current cache in concurrency safe manner.
// If reset is set to true, all inserts and deletions are working on empty cache. In such case
// this implementation tries to reuse memory from existing cached item when possible.
//
// Update reuses insert struct memory, so after use, Insert slice and its elements cannot be reused
// outside of this method.
// TODO(bwplotka): Lack of copying can pose memory safety problems if insert variables are reused. Consider copying if value
// is different. Yet it gives significant allocation gains.
func (c *CachedTGatherer) Update(reset bool, inserts []Insert, deletions []Key) error {
c.mMu.Lock()
defer c.mMu.Unlock()
Expand All @@ -126,48 +131,48 @@ func (c *CachedTGatherer) Update(reset bool, inserts []Insert, deletions []Key)
}

errs := prometheus.MultiError{}
for _, ins := range inserts {
for i := range inserts {
// TODO(bwplotka): Validate more about this insert?
if err := ins.isValid(); err != nil {
if err := inserts[i].isValid(); err != nil {
errs.Append(err)
continue
}

// Update metric family.
mf, ok := c.metricFamilyByName[ins.FQName]
mf, ok := c.metricFamilyByName[inserts[i].FQName]
if !ok {
mf = &dto.MetricFamily{}
mf.Name = proto.String(ins.FQName)
mf.Name = &inserts[i].FQName
} else if reset {
// Reset metric slice, since we want to start from scratch.
mf.Metric = mf.Metric[:0]
}
mf.Type = ins.ValueType.ToDTO()
mf.Help = proto.String(ins.Help)
mf.Type = inserts[i].ValueType.ToDTO()
mf.Help = &inserts[i].Help

currMetricFamilies[ins.FQName] = mf
currMetricFamilies[inserts[i].FQName] = mf

// Update metric pointer.
hSum := ins.hash()
hSum := inserts[i].hash()
m, ok := c.metrics[hSum]
if !ok {
m = &dto.Metric{Label: make([]*dto.LabelPair, 0, len(ins.LabelNames))}
for i := range ins.LabelNames {
m = &dto.Metric{Label: make([]*dto.LabelPair, 0, len(inserts[i].LabelNames))}
for j := range inserts[i].LabelNames {
m.Label = append(m.Label, &dto.LabelPair{
Name: proto.String(ins.LabelNames[i]),
Value: proto.String(ins.LabelValues[i]),
Name: &inserts[i].LabelNames[j],
Value: &inserts[i].LabelValues[j],
})
}
sort.Sort(labelPairSorter(m.Label))
sort.Sort(internal.LabelPairSorter(m.Label))
}

switch ins.ValueType {
switch inserts[i].ValueType {
case prometheus.CounterValue:
v := m.Counter
if v == nil {
v = &dto.Counter{}
}
v.Value = proto.Float64(ins.Value)
v.Value = &inserts[i].Value
m.Counter = v
m.Gauge = nil
m.Untyped = nil
Expand All @@ -176,7 +181,7 @@ func (c *CachedTGatherer) Update(reset bool, inserts []Insert, deletions []Key)
if v == nil {
v = &dto.Gauge{}
}
v.Value = proto.Float64(ins.Value)
v.Value = &inserts[i].Value
m.Counter = nil
m.Gauge = v
m.Untyped = nil
Expand All @@ -185,17 +190,17 @@ func (c *CachedTGatherer) Update(reset bool, inserts []Insert, deletions []Key)
if v == nil {
v = &dto.Untyped{}
}
v.Value = proto.Float64(ins.Value)
v.Value = &inserts[i].Value
m.Counter = nil
m.Gauge = nil
m.Untyped = v
default:
return fmt.Errorf("unsupported value type %v", ins.ValueType)
return fmt.Errorf("unsupported value type %v", inserts[i].ValueType)
}

m.TimestampMs = nil
if ins.Timestamp != nil {
m.TimestampMs = proto.Int64(ins.Timestamp.Unix()*1000 + int64(ins.Timestamp.Nanosecond()/1000000))
if inserts[i].Timestamp != nil {
m.TimestampMs = proto.Int64(inserts[i].Timestamp.Unix()*1000 + int64(inserts[i].Timestamp.Nanosecond()/1000000))
}
currMetrics[hSum] = m

Expand Down Expand Up @@ -254,19 +259,3 @@ func (c *CachedTGatherer) Update(reset bool, inserts []Insert, deletions []Key)
c.metricFamilyByName = currMetricFamilies
return errs.MaybeUnwrap()
}

// labelPairSorter implements sort.Interface. It is used to sort a slice of
// dto.LabelPair pointers.
type labelPairSorter []*dto.LabelPair

func (s labelPairSorter) Len() int {
return len(s)
}

func (s labelPairSorter) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}

func (s labelPairSorter) Less(i, j int) bool {
return s[i].GetName() < s[j].GetName()
}
4 changes: 3 additions & 1 deletion prometheus/desc.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import (
"strings"

"github.com/cespare/xxhash/v2"
"github.com/prometheus/client_golang/prometheus/internal"

//nolint:staticcheck // Ignore SA1019. Need to keep deprecated package for compatibility.
"github.com/golang/protobuf/proto"
"github.com/prometheus/common/model"
Expand Down Expand Up @@ -154,7 +156,7 @@ func NewDesc(fqName, help string, variableLabels []string, constLabels Labels) *
Value: proto.String(v),
})
}
sort.Sort(labelPairSorter(d.constLabelPairs))
sort.Sort(internal.LabelPairSorter(d.constLabelPairs))
return d
}

Expand Down
16 changes: 16 additions & 0 deletions prometheus/internal/metric.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,22 @@ import (
dto "github.com/prometheus/client_model/go"
)

// LabelPairSorter implements sort.Interface. It is used to sort a slice of
// dto.LabelPair pointers.
type LabelPairSorter []*dto.LabelPair

func (s LabelPairSorter) Len() int {
return len(s)
}

func (s LabelPairSorter) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}

func (s LabelPairSorter) Less(i, j int) bool {
return s[i].GetName() < s[j].GetName()
}

// metricSorter is a sortable slice of *dto.Metric.
type metricSorter []*dto.Metric

Expand Down
16 changes: 0 additions & 16 deletions prometheus/metric.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,22 +115,6 @@ func BuildFQName(namespace, subsystem, name string) string {
return name
}

// labelPairSorter implements sort.Interface. It is used to sort a slice of
// dto.LabelPair pointers.
type labelPairSorter []*dto.LabelPair

func (s labelPairSorter) Len() int {
return len(s)
}

func (s labelPairSorter) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}

func (s labelPairSorter) Less(i, j int) bool {
return s[i].GetName() < s[j].GetName()
}

type invalidMetric struct {
desc *Desc
err error
Expand Down
6 changes: 3 additions & 3 deletions prometheus/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -891,11 +891,11 @@ func checkMetricConsistency(
h.Write(separatorByteSlice)
// Make sure label pairs are sorted. We depend on it for the consistency
// check.
if !sort.IsSorted(labelPairSorter(dtoMetric.Label)) {
if !sort.IsSorted(internal.LabelPairSorter(dtoMetric.Label)) {
// We cannot sort dtoMetric.Label in place as it is immutable by contract.
copiedLabels := make([]*dto.LabelPair, len(dtoMetric.Label))
copy(copiedLabels, dtoMetric.Label)
sort.Sort(labelPairSorter(copiedLabels))
sort.Sort(internal.LabelPairSorter(copiedLabels))
dtoMetric.Label = copiedLabels
}
for _, lp := range dtoMetric.Label {
Expand Down Expand Up @@ -942,7 +942,7 @@ func checkDescConsistency(
metricFamily.GetName(), dtoMetric, desc,
)
}
sort.Sort(labelPairSorter(lpsFromDesc))
sort.Sort(internal.LabelPairSorter(lpsFromDesc))
for i, lpFromDesc := range lpsFromDesc {
lpFromMetric := dtoMetric.Label[i]
if lpFromDesc.GetName() != lpFromMetric.GetName() ||
Expand Down
3 changes: 2 additions & 1 deletion prometheus/value.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
//nolint:staticcheck // Ignore SA1019. Need to keep deprecated package for compatibility.
"github.com/golang/protobuf/proto"
"github.com/golang/protobuf/ptypes"
"github.com/prometheus/client_golang/prometheus/internal"

dto "github.com/prometheus/client_model/go"
)
Expand Down Expand Up @@ -193,7 +194,7 @@ func MakeLabelPairs(desc *Desc, labelValues []string) []*dto.LabelPair {
})
}
labelPairs = append(labelPairs, desc.constLabelPairs...)
sort.Sort(labelPairSorter(labelPairs))
sort.Sort(internal.LabelPairSorter(labelPairs))
return labelPairs
}

Expand Down
3 changes: 2 additions & 1 deletion prometheus/wrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
//nolint:staticcheck // Ignore SA1019. Need to keep deprecated package for compatibility.
"github.com/golang/protobuf/proto"

"github.com/prometheus/client_golang/prometheus/internal"
dto "github.com/prometheus/client_model/go"
)

Expand Down Expand Up @@ -182,7 +183,7 @@ func (m *wrappingMetric) Write(out *dto.Metric) error {
Value: proto.String(lv),
})
}
sort.Sort(labelPairSorter(out.Label))
sort.Sort(internal.LabelPairSorter(out.Label))
return nil
}

Expand Down

0 comments on commit 84fe356

Please sign in to comment.