From 9f82e51c894278a30980deae2639f005b8fedea2 Mon Sep 17 00:00:00 2001 From: Warnar Boekkooi Date: Tue, 3 Dec 2024 09:46:16 +0100 Subject: [PATCH] Performance improvements for attribute value `AsStringSlice`, `AsFloat64Slice`, `AsInt64Slice`, `AsBoolSlice` (#6011) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Good day, This PR changes As[Bool|Int64|Float64|String]Slice to use a little less reflection. The benchstat result of this is as follows. ``` goos: linux goarch: amd64 pkg: go.opentelemetry.io/otel/internal/attribute cpu: AMD Ryzen 7 Pro 7735U with Radeon Graphics │ org.txt │ new.txt │ │ sec/op │ sec/op vs base │ AsFloat64Slice-16 373.3n ± 41% 181.0n ± 42% -51.51% (p=0.000 n=10) │ org.txt │ new.txt │ │ B/op │ B/op vs base │ AsFloat64Slice-16 64.00 ± 0% 40.00 ± 0% -37.50% (p=0.000 n=10) │ org.txt │ new.txt │ │ allocs/op │ allocs/op vs base │ AsFloat64Slice-16 3.000 ± 0% 2.000 ± 0% -33.33% (p=0.000 n=10) ``` --------- Co-authored-by: Damien Mathieu <42@dmathieu.com> --- CHANGELOG.md | 1 + internal/attribute/attribute.go | 44 +++++++++++++--------------- internal/attribute/attribute_test.go | 10 +++++++ 3 files changed, 31 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a19036cae..9d1073c1e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - Propagate non-retryable error messages to client in `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp`. (#5929) - Propagate non-retryable error messages to client in `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp`. (#5929) - Propagate non-retryable error messages to client in `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp`. (#5929) +- Performance improvements for attribute value `AsStringSlice`, `AsFloat64Slice`, `AsInt64Slice`, `AsBoolSlice`. (#6011) ### Fixed diff --git a/internal/attribute/attribute.go b/internal/attribute/attribute.go index 822d84794..691d96c75 100644 --- a/internal/attribute/attribute.go +++ b/internal/attribute/attribute.go @@ -49,12 +49,11 @@ func AsBoolSlice(v interface{}) []bool { if rv.Type().Kind() != reflect.Array { return nil } - var zero bool - correctLen := rv.Len() - correctType := reflect.ArrayOf(correctLen, reflect.TypeOf(zero)) - cpy := reflect.New(correctType) - _ = reflect.Copy(cpy.Elem(), rv) - return cpy.Elem().Slice(0, correctLen).Interface().([]bool) + cpy := make([]bool, rv.Len()) + if len(cpy) > 0 { + _ = reflect.Copy(reflect.ValueOf(cpy), rv) + } + return cpy } // AsInt64Slice converts an int64 array into a slice into with same elements as array. @@ -63,12 +62,11 @@ func AsInt64Slice(v interface{}) []int64 { if rv.Type().Kind() != reflect.Array { return nil } - var zero int64 - correctLen := rv.Len() - correctType := reflect.ArrayOf(correctLen, reflect.TypeOf(zero)) - cpy := reflect.New(correctType) - _ = reflect.Copy(cpy.Elem(), rv) - return cpy.Elem().Slice(0, correctLen).Interface().([]int64) + cpy := make([]int64, rv.Len()) + if len(cpy) > 0 { + _ = reflect.Copy(reflect.ValueOf(cpy), rv) + } + return cpy } // AsFloat64Slice converts a float64 array into a slice into with same elements as array. @@ -77,12 +75,11 @@ func AsFloat64Slice(v interface{}) []float64 { if rv.Type().Kind() != reflect.Array { return nil } - var zero float64 - correctLen := rv.Len() - correctType := reflect.ArrayOf(correctLen, reflect.TypeOf(zero)) - cpy := reflect.New(correctType) - _ = reflect.Copy(cpy.Elem(), rv) - return cpy.Elem().Slice(0, correctLen).Interface().([]float64) + cpy := make([]float64, rv.Len()) + if len(cpy) > 0 { + _ = reflect.Copy(reflect.ValueOf(cpy), rv) + } + return cpy } // AsStringSlice converts a string array into a slice into with same elements as array. @@ -91,10 +88,9 @@ func AsStringSlice(v interface{}) []string { if rv.Type().Kind() != reflect.Array { return nil } - var zero string - correctLen := rv.Len() - correctType := reflect.ArrayOf(correctLen, reflect.TypeOf(zero)) - cpy := reflect.New(correctType) - _ = reflect.Copy(cpy.Elem(), rv) - return cpy.Elem().Slice(0, correctLen).Interface().([]string) + cpy := make([]string, rv.Len()) + if len(cpy) > 0 { + _ = reflect.Copy(reflect.ValueOf(cpy), rv) + } + return cpy } diff --git a/internal/attribute/attribute_test.go b/internal/attribute/attribute_test.go index 113b644c8..e2c35d970 100644 --- a/internal/attribute/attribute_test.go +++ b/internal/attribute/attribute_test.go @@ -133,3 +133,13 @@ func BenchmarkStringSliceValue(b *testing.B) { sync = StringSliceValue(s) } } + +func BenchmarkAsFloat64Slice(b *testing.B) { + b.ReportAllocs() + var in interface{} = [2]float64{1, 2.3} + b.ResetTimer() + + for i := 0; i < b.N; i++ { + sync = AsFloat64Slice(in) + } +}