diff --git a/attribute/set.go b/attribute/set.go index 9f9303d4f15d..cd61ad52335c 100644 --- a/attribute/set.go +++ b/attribute/set.go @@ -18,7 +18,6 @@ import ( "encoding/json" "reflect" "sort" - "sync" ) type ( @@ -56,12 +55,6 @@ 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,9 +184,7 @@ func NewSet(kvs ...KeyValue) Set { if len(kvs) == 0 { return empty() } - srt := sortables.Get().(*Sortable) - s, _ := NewSetWithSortableFiltered(kvs, srt, nil) - sortables.Put(srt) + s, _ := NewSetWithSortableFiltered(kvs, nil, nil) return s } @@ -201,12 +192,12 @@ func NewSet(kvs ...KeyValue) Set { // NewSetWithSortableFiltered for more details. // // This call includes a Sortable option as a memory optimization. -func NewSetWithSortable(kvs []KeyValue, tmp *Sortable) Set { +func NewSetWithSortable(kvs []KeyValue, _ *Sortable) Set { // Check for empty set. if len(kvs) == 0 { return empty() } - s, _ := NewSetWithSortableFiltered(kvs, tmp, nil) + s, _ := NewSetWithSortableFiltered(kvs, nil, nil) return s } @@ -220,9 +211,7 @@ func NewSetWithFiltered(kvs []KeyValue, filter Filter) (Set, []KeyValue) { if len(kvs) == 0 { return empty(), nil } - srt := sortables.Get().(*Sortable) - s, filtered := NewSetWithSortableFiltered(kvs, srt, filter) - sortables.Put(srt) + s, filtered := NewSetWithSortableFiltered(kvs, nil, filter) return s, filtered } @@ -249,19 +238,15 @@ func NewSetWithFiltered(kvs []KeyValue, filter Filter) (Set, []KeyValue) { // // The second []KeyValue return value is a list of attributes that were // excluded by the Filter (if non-nil). -func NewSetWithSortableFiltered(kvs []KeyValue, tmp *Sortable, filter Filter) (Set, []KeyValue) { +func NewSetWithSortableFiltered(kvs []KeyValue, _ *Sortable, filter Filter) (Set, []KeyValue) { // Check for empty set. if len(kvs) == 0 { return empty(), nil } - *tmp = kvs - // Stable sort so the following de-duplication can implement // last-value-wins semantics. - sort.Stable(tmp) - - *tmp = nil + sliceSortStable(kvs) position := len(kvs) - 1 offset := position - 1 diff --git a/attribute/set_test.go b/attribute/set_test.go index 4fb47752e5f4..105e3f77cb70 100644 --- a/attribute/set_test.go +++ b/attribute/set_test.go @@ -225,3 +225,18 @@ func args(m reflect.Method) []reflect.Value { } return out } + +var ( + sinkSet attribute.Set + sinkAttrs []attribute.KeyValue +) + +func BenchmarkNewSetWithSortableFiltered(b *testing.B) { + attrs := []attribute.KeyValue{attribute.Int("C", 3), attribute.Int("A", 1), attribute.Int("B", 2)} + srt := new(attribute.Sortable) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + sinkSet, sinkAttrs = attribute.NewSetWithSortableFiltered(attrs, srt, nil) + } +} diff --git a/attribute/sort.go b/attribute/sort.go new file mode 100644 index 000000000000..7a01003be3df --- /dev/null +++ b/attribute/sort.go @@ -0,0 +1,11 @@ +//go:build !go1.21 + +package attribute + +import "sort" + +func sliceSortStable(kvs []KeyValue) { + sort.SliceStable(kvs, func(i, j int) bool { + return kvs[i].Key < kvs[j].Key + }) +} diff --git a/attribute/sort_1.21.go b/attribute/sort_1.21.go new file mode 100644 index 000000000000..003526dd96aa --- /dev/null +++ b/attribute/sort_1.21.go @@ -0,0 +1,17 @@ +//go:build go1.21 + +package attribute + +import "slices" + +func sliceSortStable(kvs []KeyValue) { + slices.SortStableFunc(kvs, func(a, b KeyValue) int { + if a.Key == b.Key { + return 0 + } + if a.Key < b.Key { + return -1 + } + return 1 + }) +}