Skip to content

Commit

Permalink
fastdto attempt.
Browse files Browse the repository at this point in the history
Signed-off-by: bwplotka <[email protected]>
  • Loading branch information
bwplotka committed Feb 20, 2025
1 parent 6da4db6 commit d2d077e
Show file tree
Hide file tree
Showing 12 changed files with 238 additions and 125 deletions.
3 changes: 2 additions & 1 deletion prometheus/counter.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"sync/atomic"
"time"

"github.com/prometheus/client_golang/prometheus/internal/fastdto"

Check failure on line 22 in prometheus/counter.go

View workflow job for this annotation

GitHub Actions / lint

File is not properly formatted (goimports)

Check failure on line 22 in prometheus/counter.go

View workflow job for this annotation

GitHub Actions / lint

File is not properly formatted (goimports)
dto "github.com/prometheus/client_model/go"
"google.golang.org/protobuf/types/known/timestamppb"
)
Expand Down Expand Up @@ -94,7 +95,7 @@ func NewCounter(opts CounterOpts) Counter {
if opts.now == nil {
opts.now = time.Now
}
result := &counter{desc: desc, labelPairs: desc.labelPairs, now: opts.now}
result := &counter{desc: desc, labelPairs: fastdto.ToDTOLabelPair(desc.labelPairs), now: opts.now}
result.init(result) // Init self-collection.
result.createdTs = timestamppb.New(opts.now())
return result
Expand Down
29 changes: 12 additions & 17 deletions prometheus/desc.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,8 @@ import (
"strings"

"github.com/cespare/xxhash/v2"
dto "github.com/prometheus/client_model/go"
"github.com/prometheus/client_golang/prometheus/internal/fastdto"

Check failure on line 22 in prometheus/desc.go

View workflow job for this annotation

GitHub Actions / lint

File is not properly formatted (goimports)

Check failure on line 22 in prometheus/desc.go

View workflow job for this annotation

GitHub Actions / lint

File is not properly formatted (goimports)
"github.com/prometheus/common/model"
"google.golang.org/protobuf/proto"

"github.com/prometheus/client_golang/prometheus/internal"
)

// Desc is the descriptor used by every Prometheus Metric. It is essentially
Expand Down Expand Up @@ -56,7 +53,7 @@ type Desc struct {
variableLabelOrder []int
// labelPairs contains the sorted DTO label pairs based on the constant labels
// and variable labels
labelPairs []*dto.LabelPair
labelPairs []fastdto.LabelPair
// id is a hash of the values of the ConstLabels and fqName. This
// must be unique among all registered descriptors and can therefore be
// used as an identifier of the descriptor.
Expand Down Expand Up @@ -164,24 +161,23 @@ func (v2) NewDesc(fqName, help string, variableLabels ConstrainableLabels, const
}
d.dimHash = xxh.Sum64()

d.labelPairs = make([]*dto.LabelPair, 0, len(constLabels)+len(d.variableLabels.names))
d.labelPairs = make([]fastdto.LabelPair, len(constLabels)+len(d.variableLabels.names))
i := 0
for n, v := range constLabels {
d.labelPairs = append(d.labelPairs, &dto.LabelPair{
Name: proto.String(n),
Value: proto.String(v),
})
d.labelPairs[i].Name = n
d.labelPairs[i].Value = v
i++
}
for _, labelName := range d.variableLabels.names {
d.labelPairs = append(d.labelPairs, &dto.LabelPair{
Name: proto.String(labelName),
})
d.labelPairs[i].Name = labelName
i++
}
sort.Sort(internal.LabelPairSorter(d.labelPairs))
sort.Sort(fastdto.LabelPairSorter(d.labelPairs))

d.variableLabelOrder = make([]int, len(d.variableLabels.names))
for outputIndex, pair := range d.labelPairs {
// Constant labels have values variable labels do not.
if pair.Value != nil {
if pair.Value != "" {
continue
}
for sourceIndex, variableLabel := range d.variableLabels.names {
Expand All @@ -190,7 +186,6 @@ func (v2) NewDesc(fqName, help string, variableLabels ConstrainableLabels, const
}
}
}

return d
}

Expand All @@ -207,7 +202,7 @@ func NewInvalidDesc(err error) *Desc {
func (d *Desc) String() string {
lpStrings := make([]string, 0, len(d.labelPairs))
for _, lp := range d.labelPairs {
if lp.Value == nil {
if lp.Value == "" {
continue
}
lpStrings = append(
Expand Down
14 changes: 14 additions & 0 deletions prometheus/desc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,18 @@ func TestNewInvalidDesc_String(t *testing.T) {
}
}

/*
export bench=newDesc && go test ./prometheus \

Check failure on line 67 in prometheus/desc_test.go

View workflow job for this annotation

GitHub Actions / lint

File is not properly formatted (gofmt)

Check failure on line 67 in prometheus/desc_test.go

View workflow job for this annotation

GitHub Actions / lint

File is not properly formatted (gofmt)
-run '^$' -bench '^BenchmarkNewDesc/labels=10' \
-benchtime 5s -benchmem -cpu 2 -timeout 999m \
-memprofile=${bench}.mem.pprof \
| tee ${bench}.txt
export bench=newDesc-v2 && go test ./prometheus \

Check failure on line 73 in prometheus/desc_test.go

View workflow job for this annotation

GitHub Actions / lint

File is not properly formatted (gofumpt)

Check failure on line 73 in prometheus/desc_test.go

View workflow job for this annotation

GitHub Actions / lint

File is not properly formatted (gofumpt)
-run '^$' -bench '^BenchmarkNewDesc' \
-benchtime 5s -benchmem -count=6 -cpu 2 -timeout 999m \
| tee ${bench}.txt
*/
func BenchmarkNewDesc(b *testing.B) {
for _, bm := range []struct {
labelCount int
Expand All @@ -82,6 +94,8 @@ func BenchmarkNewDesc(b *testing.B) {
},
} {
b.Run(fmt.Sprintf("labels=%v", bm.labelCount), func(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
bm.descFunc()
}
Expand Down
3 changes: 2 additions & 1 deletion prometheus/gauge.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"sync/atomic"
"time"

"github.com/prometheus/client_golang/prometheus/internal/fastdto"

Check failure on line 21 in prometheus/gauge.go

View workflow job for this annotation

GitHub Actions / lint

File is not properly formatted (goimports)

Check failure on line 21 in prometheus/gauge.go

View workflow job for this annotation

GitHub Actions / lint

File is not properly formatted (goimports)
dto "github.com/prometheus/client_model/go"
)

Expand Down Expand Up @@ -82,7 +83,7 @@ func NewGauge(opts GaugeOpts) Gauge {
nil,
opts.ConstLabels,
)
result := &gauge{desc: desc, labelPairs: desc.labelPairs}
result := &gauge{desc: desc, labelPairs: fastdto.ToDTOLabelPair(desc.labelPairs)}
result.init(result) // Init self-collection.
return result
}
Expand Down
88 changes: 44 additions & 44 deletions prometheus/histogram.go
Original file line number Diff line number Diff line change
Expand Up @@ -956,13 +956,13 @@ func (h *histogram) limitBuckets(counts *histogramCounts, value float64, bucket
// h.nativeHistogramMinResetDuration has been passed. It returns true if the
// histogram has been reset. The caller must have locked h.mtx.
func (h *histogram) maybeReset(
hot, cold *histogramCounts, coldIdx uint64, value float64, bucket int,
hot, cold *histogramCounts, coldIdx uint64, value float64, bucket int,

Check failure on line 959 in prometheus/histogram.go

View workflow job for this annotation

GitHub Actions / lint

File is not properly formatted (gofmt)

Check failure on line 959 in prometheus/histogram.go

View workflow job for this annotation

GitHub Actions / lint

File is not properly formatted (gofmt)
) bool {
// We are using the possibly mocked h.now() rather than
// time.Since(h.lastResetTime) to enable testing.
if h.nativeHistogramMinResetDuration == 0 || // No reset configured.
h.resetScheduled || // Do not interefere if a reset is already scheduled.
h.now().Sub(h.lastResetTime) < h.nativeHistogramMinResetDuration {
h.resetScheduled || // Do not interefere if a reset is already scheduled.
h.now().Sub(h.lastResetTime) < h.nativeHistogramMinResetDuration {
return false
}
// Completely reset coldCounts.
Expand Down Expand Up @@ -1355,11 +1355,11 @@ func (h *constHistogram) Write(out *dto.Metric) error {
// NewConstHistogram returns an error if the length of labelValues is not
// consistent with the variable labels in Desc or if Desc is invalid.
func NewConstHistogram(
desc *Desc,
count uint64,
sum float64,
buckets map[float64]uint64,
labelValues ...string,
desc *Desc,
count uint64,
sum float64,
buckets map[float64]uint64,
labelValues ...string,
) (Metric, error) {
if desc.err != nil {
return nil, desc.err
Expand All @@ -1379,11 +1379,11 @@ func NewConstHistogram(
// MustNewConstHistogram is a version of NewConstHistogram that panics where
// NewConstHistogram would have returned an error.
func MustNewConstHistogram(
desc *Desc,
count uint64,
sum float64,
buckets map[float64]uint64,
labelValues ...string,
desc *Desc,
count uint64,
sum float64,
buckets map[float64]uint64,
labelValues ...string,
) Metric {
m, err := NewConstHistogram(desc, count, sum, buckets, labelValues...)
if err != nil {
Expand All @@ -1394,12 +1394,12 @@ func MustNewConstHistogram(

// NewConstHistogramWithCreatedTimestamp does the same thing as NewConstHistogram but sets the created timestamp.
func NewConstHistogramWithCreatedTimestamp(
desc *Desc,
count uint64,
sum float64,
buckets map[float64]uint64,
ct time.Time,
labelValues ...string,
desc *Desc,
count uint64,
sum float64,
buckets map[float64]uint64,
ct time.Time,
labelValues ...string,
) (Metric, error) {
if desc.err != nil {
return nil, desc.err
Expand All @@ -1420,12 +1420,12 @@ func NewConstHistogramWithCreatedTimestamp(
// MustNewConstHistogramWithCreatedTimestamp is a version of NewConstHistogramWithCreatedTimestamp that panics where
// NewConstHistogramWithCreatedTimestamp would have returned an error.
func MustNewConstHistogramWithCreatedTimestamp(
desc *Desc,
count uint64,
sum float64,
buckets map[float64]uint64,
ct time.Time,
labelValues ...string,
desc *Desc,
count uint64,
sum float64,
buckets map[float64]uint64,
ct time.Time,
labelValues ...string,
) Metric {
m, err := NewConstHistogramWithCreatedTimestamp(desc, count, sum, buckets, ct, labelValues...)
if err != nil {
Expand Down Expand Up @@ -1873,7 +1873,7 @@ func validateCount(sum float64, count uint64, negativeBuckets, positiveBuckets m
// Otherwise, the number of observations must be equal to the sum of all bucket counts .

if math.IsNaN(sum) && bucketPopulationSum > int64(count) ||
!math.IsNaN(sum) && bucketPopulationSum != int64(count) {
!math.IsNaN(sum) && bucketPopulationSum != int64(count) {
return errors.New("the sum of all bucket populations exceeds the count of observations")
}
return nil
Expand Down Expand Up @@ -1902,15 +1902,15 @@ func validateCount(sum float64, count uint64, negativeBuckets, positiveBuckets m
//
// See https://opentelemetry.io/docs/specs/otel/compatibility/prometheus_and_openmetrics/#exponential-histograms for more details about the conversion from OTel to Prometheus.
func NewConstNativeHistogram(
desc *Desc,
count uint64,
sum float64,
positiveBuckets, negativeBuckets map[int]int64,
zeroBucket uint64,
schema int32,
zeroThreshold float64,
createdTimestamp time.Time,
labelValues ...string,
desc *Desc,
count uint64,
sum float64,
positiveBuckets, negativeBuckets map[int]int64,
zeroBucket uint64,
schema int32,
zeroThreshold float64,
createdTimestamp time.Time,
labelValues ...string,
) (Metric, error) {
if desc.err != nil {
return nil, desc.err
Expand Down Expand Up @@ -1958,15 +1958,15 @@ func NewConstNativeHistogram(
// MustNewConstNativeHistogram is a version of NewConstNativeHistogram that panics where
// NewConstNativeHistogram would have returned an error.
func MustNewConstNativeHistogram(
desc *Desc,
count uint64,
sum float64,
positiveBuckets, negativeBuckets map[int]int64,
zeroBucket uint64,
nativeHistogramSchema int32,
nativeHistogramZeroThreshold float64,
createdTimestamp time.Time,
labelValues ...string,
desc *Desc,
count uint64,
sum float64,
positiveBuckets, negativeBuckets map[int]int64,
zeroBucket uint64,
nativeHistogramSchema int32,
nativeHistogramZeroThreshold float64,
createdTimestamp time.Time,
labelValues ...string,
) Metric {
nativehistogram, err := NewConstNativeHistogram(desc,
count,
Expand Down
45 changes: 45 additions & 0 deletions prometheus/internal/fastdto/labels.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package fastdto

import (
dto "github.com/prometheus/client_model/go"
)

type LabelPair struct {
Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
Value string `protobuf:"bytes,2,opt,name=value" json:"value,omitempty"`
}

func (p LabelPair) GetName() string {
return p.Name
}

func (p LabelPair) GetValue() string {
return p.Value
}

// LabelPairSorter implements sort.Interface. It is used to sort a slice of
// LabelPairs
type LabelPairSorter []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].Name < s[j].Name
}

func ToDTOLabelPair(in []LabelPair) []*dto.LabelPair {
ret := make([]*dto.LabelPair, len(in))
for i := range in {
ret[i] = &dto.LabelPair{
Name: &(in[i].Name),
Value: &(in[i].Value),
}
}
return ret
}
58 changes: 58 additions & 0 deletions prometheus/internal/fastdto/labels_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package fastdto

import (
"sort"
"testing"

"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
dto "github.com/prometheus/client_model/go"
"google.golang.org/protobuf/proto"
)

func BenchmarkToDTOLabelPairs(b *testing.B) {
test := []LabelPair{
{"foo", "bar"},
{"foo2", "bar2"},
{"foo3", "bar3"},
}
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = ToDTOLabelPair(test)
}
}

func TestLabelPairSorter(t *testing.T) {
test := []LabelPair{
{"foo3", "bar3"},
{"foo", "bar"},
{"foo2", "bar2"},
}
sort.Sort(LabelPairSorter(test))

expected := []LabelPair{
{"foo", "bar"},
{"foo2", "bar2"},
{"foo3", "bar3"},
}
if diff := cmp.Diff(test, expected); diff != "" {
t.Fatal(diff)
}
}

func TestToDTOLabelPair(t *testing.T) {
test := []LabelPair{
{"foo", "bar"},
{"foo2", "bar2"},
{"foo3", "bar3"},
}
expected := []*dto.LabelPair{
{Name: proto.String("foo"), Value: proto.String("bar")},
{Name: proto.String("foo2"), Value: proto.String("bar2")},
{Name: proto.String("foo3"), Value: proto.String("bar3")},
}
if diff := cmp.Diff(ToDTOLabelPair(test), expected, cmpopts.IgnoreUnexported(dto.LabelPair{})); diff != "" {
t.Fatal(diff)
}
}
Loading

0 comments on commit d2d077e

Please sign in to comment.