1
0
mirror of https://github.com/open-telemetry/opentelemetry-go.git synced 2025-07-17 01:12:45 +02:00

Pool sortables used to create attribute sets (#3832)

* Pool sortables used to create attribute sets

* Move sync pool to attribute pkg

* Add change to changelog

* Fix comment

* Apply suggestions from code review

Co-authored-by: Peter Liu <lpfvip2008@gmail.com>

* Update sdk/metric/instrument.go

Co-authored-by: Robert Pająk <pellared@hotmail.com>

* Update comment based on feedback

* Apply feedback

---------

Co-authored-by: Peter Liu <lpfvip2008@gmail.com>
Co-authored-by: Robert Pająk <pellared@hotmail.com>
This commit is contained in:
Tyler Yahn
2023-03-13 11:19:28 -07:00
committed by GitHub
parent 4af35a97dd
commit b62eb2ca88
4 changed files with 88 additions and 4 deletions

View File

@ -14,6 +14,8 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
### Changed
- Optimize memory allocation when creation a new `Set` using `NewSet` or `NewSetWithFiltered` in `go.opentelemetry.io/otel/attribute`. (#3832)
- Optimize memory allocation when creation new metric instruments in `go.opentelemetry.io/otel/sdk/metric`. (#3832)
- Avoid creating new objects on all calls to `WithDeferredSetup` and `SkipContextSetup` in OpenTracing bridge. (#3833)
- The `New` and `Detect` functions from `go.opentelemetry.io/otel/sdk/resource` return errors that wrap underlying errors instead of just containing the underlying error strings. (#3844)

View File

@ -18,6 +18,7 @@ import (
"encoding/json"
"reflect"
"sort"
"sync"
)
type (
@ -62,6 +63,12 @@ var (
iface: [0]KeyValue{},
},
}
// sortables is a pool of Sortables used to create Sets with a user does
// not provide one.
sortables = sync.Pool{
New: func() interface{} { return new(Sortable) },
}
)
// EmptySet returns a reference to a Set with no elements.
@ -191,7 +198,9 @@ func NewSet(kvs ...KeyValue) Set {
if len(kvs) == 0 {
return empty()
}
s, _ := NewSetWithSortableFiltered(kvs, new(Sortable), nil)
srt := sortables.Get().(*Sortable)
s, _ := NewSetWithSortableFiltered(kvs, srt, nil)
sortables.Put(srt)
return s
}
@ -218,7 +227,10 @@ func NewSetWithFiltered(kvs []KeyValue, filter Filter) (Set, []KeyValue) {
if len(kvs) == 0 {
return empty(), nil
}
return NewSetWithSortableFiltered(kvs, new(Sortable), filter)
srt := sortables.Get().(*Sortable)
s, filtered := NewSetWithSortableFiltered(kvs, srt, filter)
sortables.Put(srt)
return s, filtered
}
// NewSetWithSortableFiltered returns a new Set.

View File

@ -194,8 +194,12 @@ func (i *instrumentImpl[N]) aggregate(ctx context.Context, val N, attrs []attrib
if err := ctx.Err(); err != nil {
return
}
// Do not use single attribute.Sortable and attribute.NewSetWithSortable,
// this method needs to be concurrent safe. Let the sync.Pool in the
// attribute package handle allocations of the Sortable.
s := attribute.NewSet(attrs...)
for _, agg := range i.aggregators {
agg.Aggregate(val, attribute.NewSet(attrs...))
agg.Aggregate(val, s)
}
}
@ -260,8 +264,12 @@ func newObservable[N int64 | float64](scope instrumentation.Scope, kind Instrume
// observe records the val for the set of attrs.
func (o *observable[N]) observe(val N, attrs []attribute.KeyValue) {
// Do not use single attribute.Sortable and attribute.NewSetWithSortable,
// this method needs to be concurrent safe. Let the sync.Pool in the
// attribute package handle allocations of the Sortable.
s := attribute.NewSet(attrs...)
for _, agg := range o.aggregators {
agg.Aggregate(val, attribute.NewSet(attrs...))
agg.Aggregate(val, s)
}
}

View File

@ -0,0 +1,62 @@
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package metric
import (
"context"
"testing"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/sdk/metric/internal"
)
func BenchmarkInstrument(b *testing.B) {
attr := func(id int) []attribute.KeyValue {
return []attribute.KeyValue{
attribute.String("user", "Alice"),
attribute.Bool("admin", true),
attribute.Int("id", id),
}
}
b.Run("instrumentImpl/aggregate", func(b *testing.B) {
inst := instrumentImpl[int64]{aggregators: []internal.Aggregator[int64]{
internal.NewLastValue[int64](),
internal.NewCumulativeSum[int64](true),
internal.NewDeltaSum[int64](true),
}}
ctx := context.Background()
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
inst.aggregate(ctx, int64(i), attr(i))
}
})
b.Run("observable/observe", func(b *testing.B) {
o := observable[int64]{aggregators: []internal.Aggregator[int64]{
internal.NewLastValue[int64](),
internal.NewCumulativeSum[int64](true),
internal.NewDeltaSum[int64](true),
}}
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
o.observe(int64(i), attr(i))
}
})
}