Skip to content

Commit

Permalink
[pkg/ottl] Improve time performance (#35129)
Browse files Browse the repository at this point in the history
**Description:**
Improves `Time` performance by move the conversion from our format to
Go's format to happen during startup.

Benchmarks before:

```
goos: darwin
goarch: arm64
pkg: github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/ottlfuncs
Benchmark_Time/simple_short_form-10         	1000000000	         0.0000079 ns/op	       0 B/op	       0 allocs/op
Benchmark_Time/simple_short_form_with_short_year_and_slashes-10         	1000000000	         0.0000115 ns/op	       0 B/op	       0 allocs/op
Benchmark_Time/month_day_year-10                                        	1000000000	         0.0000057 ns/op	       0 B/op	       0 allocs/op
Benchmark_Time/simple_long_form-10                                      	1000000000	         0.0000075 ns/op	       0 B/op	       0 allocs/op
Benchmark_Time/date_with_timestamp-10                                   	1000000000	         0.0000063 ns/op	       0 B/op	       0 allocs/op
Benchmark_Time/day_of_the_week_long_form-10                             	1000000000	         0.0000085 ns/op	       0 B/op	       0 allocs/op
Benchmark_Time/short_weekday,_short_month,_long_format-10               	1000000000	         0.0000089 ns/op	       0 B/op	       0 allocs/op
Benchmark_Time/short_months-10                                          	1000000000	         0.0000070 ns/op	       0 B/op	       0 allocs/op
Benchmark_Time/timestamp_with_time_zone_offset-10                       	1000000000	         0.0000665 ns/op	       0 B/op	       0 allocs/op
Benchmark_Time/short_date_with_timestamp_without_time_zone_offset-10    	1000000000	         0.0000428 ns/op	       0 B/op	       0 allocs/op
Benchmark_Time/RFC_3339_in_custom_format-10                             	1000000000	         0.0000345 ns/op	       0 B/op	       0 allocs/op
Benchmark_Time/RFC_3339_in_custom_format_before_2000-10                 	1000000000	         0.0000349 ns/op	       0 B/op	       0 allocs/op
Benchmark_Time/no_location-10                                           	1000000000	         0.0000035 ns/op	       0 B/op	       0 allocs/op
Benchmark_Time/with_location_-_America-10                               	1000000000	         0.0000104 ns/op	       0 B/op	       0 allocs/op
Benchmark_Time/with_location_-_Asia-10                                  	1000000000	         0.0000084 ns/op	       0 B/op	       0 allocs/op
Benchmark_Time/RFC_3339_in_custom_format_before_2000,_ignore_default_location-10         	1000000000	         0.0000379 ns/op	       0 B/op	       0 allocs/op
PASS
ok  	github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/ottlfuncs	0.458s
```

Benchmark's after:

```
goos: darwin
goarch: arm64
pkg: github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/ottlfuncs
Benchmark_Time/simple_short_form-10         	1000000000	         0.0000054 ns/op	       0 B/op	       0 allocs/op
Benchmark_Time/simple_short_form_with_short_year_and_slashes-10         	1000000000	         0.0000037 ns/op	       0 B/op	       0 allocs/op
Benchmark_Time/month_day_year-10                                        	1000000000	         0.0000053 ns/op	       0 B/op	       0 allocs/op
Benchmark_Time/simple_long_form-10                                      	1000000000	         0.0000042 ns/op	       0 B/op	       0 allocs/op
Benchmark_Time/date_with_timestamp-10                                   	1000000000	         0.0000087 ns/op	       0 B/op	       0 allocs/op
Benchmark_Time/day_of_the_week_long_form-10                             	1000000000	         0.0000035 ns/op	       0 B/op	       0 allocs/op
Benchmark_Time/short_weekday,_short_month,_long_format-10               	1000000000	         0.0000036 ns/op	       0 B/op	       0 allocs/op
Benchmark_Time/short_months-10                                          	1000000000	         0.0000031 ns/op	       0 B/op	       0 allocs/op
Benchmark_Time/timestamp_with_time_zone_offset-10                       	1000000000	         0.0000491 ns/op	       0 B/op	       0 allocs/op
Benchmark_Time/short_date_with_timestamp_without_time_zone_offset-10    	1000000000	         0.0000381 ns/op	       0 B/op	       0 allocs/op
Benchmark_Time/RFC_3339_in_custom_format-10                             	1000000000	         0.0000365 ns/op	       0 B/op	       0 allocs/op
Benchmark_Time/RFC_3339_in_custom_format_before_2000-10                 	1000000000	         0.0000364 ns/op	       0 B/op	       0 allocs/op
Benchmark_Time/no_location-10                                           	1000000000	         0.0000028 ns/op	       0 B/op	       0 allocs/op
Benchmark_Time/with_location_-_America-10                               	1000000000	         0.0000017 ns/op	       0 B/op	       0 allocs/op
Benchmark_Time/with_location_-_Asia-10                                  	1000000000	         0.0000028 ns/op	       0 B/op	       0 allocs/op
Benchmark_Time/RFC_3339_in_custom_format_before_2000,_ignore_default_location-10         	1000000000	         0.0000393 ns/op	       0 B/op	       0 allocs/op
PASS
ok  	github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/ottlfuncs	0.441s
```

**Link to tracking Issue:** <Issue number if applicable>
Closes
#35078

**Testing:** <Describe what testing was performed and which tests were
added.>
Added benchmark test
  • Loading branch information
TylerHelmuth authored Sep 16, 2024
1 parent e5e9e87 commit 7e8ebd7
Show file tree
Hide file tree
Showing 2 changed files with 197 additions and 2 deletions.
8 changes: 6 additions & 2 deletions pkg/ottl/ottlfuncs/func_time.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ func Time[K any](inputTime ottl.StringGetter[K], format string, location ottl.Op
if format == "" {
return nil, fmt.Errorf("format cannot be nil")
}
gotimeFormat, err := timeutils.StrptimeToGotime(format)
if err != nil {
return nil, err
}

var defaultLocation *string
if !location.IsEmpty() {
l := location.Get()
Expand All @@ -44,7 +49,6 @@ func Time[K any](inputTime ottl.StringGetter[K], format string, location ottl.Op
if err != nil {
return nil, err
}

return func(ctx context.Context, tCtx K) (any, error) {
t, err := inputTime.Get(ctx, tCtx)
if err != nil {
Expand All @@ -53,7 +57,7 @@ func Time[K any](inputTime ottl.StringGetter[K], format string, location ottl.Op
if t == "" {
return nil, fmt.Errorf("time cannot be nil")
}
timestamp, err := timeutils.ParseStrptime(format, t, loc)
timestamp, err := timeutils.ParseGotime(gotimeFormat, t, loc)
if err != nil {
return nil, err
}
Expand Down
191 changes: 191 additions & 0 deletions pkg/ottl/ottlfuncs/func_time_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -284,3 +284,194 @@ func Test_TimeFormatError(t *testing.T) {
})
}
}

func Benchmark_Time(t *testing.B) {
locationAmericaNewYork, _ := time.LoadLocation("America/New_York")
locationAsiaShanghai, _ := time.LoadLocation("Asia/Shanghai")

tests := []struct {
name string
time ottl.StringGetter[any]
format string
expected time.Time
location string
}{
{
name: "simple short form",
time: &ottl.StandardStringGetter[any]{
Getter: func(_ context.Context, _ any) (any, error) {
return "2023-04-12", nil
},
},
format: "%Y-%m-%d",
expected: time.Date(2023, 4, 12, 0, 0, 0, 0, time.Local),
},
{
name: "simple short form with short year and slashes",
time: &ottl.StandardStringGetter[any]{
Getter: func(_ context.Context, _ any) (any, error) {
return "11/11/11", nil
},
},
format: "%d/%m/%y",
expected: time.Date(2011, 11, 11, 0, 0, 0, 0, time.Local),
},
{
name: "month day year",
time: &ottl.StandardStringGetter[any]{
Getter: func(_ context.Context, _ any) (any, error) {
return "02/04/2023", nil
},
},
format: "%m/%d/%Y",
expected: time.Date(2023, 2, 4, 0, 0, 0, 0, time.Local),
},
{
name: "simple long form",
time: &ottl.StandardStringGetter[any]{
Getter: func(_ context.Context, _ any) (any, error) {
return "July 31, 1993", nil
},
},
format: "%B %d, %Y",
expected: time.Date(1993, 7, 31, 0, 0, 0, 0, time.Local),
},
{
name: "date with timestamp",
time: &ottl.StandardStringGetter[any]{
Getter: func(_ context.Context, _ any) (any, error) {
return "Mar 14 2023 17:02:59", nil
},
},
format: "%b %d %Y %H:%M:%S",
expected: time.Date(2023, 3, 14, 17, 02, 59, 0, time.Local),
},
{
name: "day of the week long form",
time: &ottl.StandardStringGetter[any]{
Getter: func(_ context.Context, _ any) (any, error) {
return "Monday, May 01, 2023", nil
},
},
format: "%A, %B %d, %Y",
expected: time.Date(2023, 5, 1, 0, 0, 0, 0, time.Local),
},
{
name: "short weekday, short month, long format",
time: &ottl.StandardStringGetter[any]{
Getter: func(_ context.Context, _ any) (any, error) {
return "Sat, May 20, 2023", nil
},
},
format: "%a, %b %d, %Y",
expected: time.Date(2023, 5, 20, 0, 0, 0, 0, time.Local),
},
{
name: "short months",
time: &ottl.StandardStringGetter[any]{
Getter: func(_ context.Context, _ any) (any, error) {
return "Feb 15, 2023", nil
},
},
format: "%b %d, %Y",
expected: time.Date(2023, 2, 15, 0, 0, 0, 0, time.Local),
},
{
name: "timestamp with time zone offset",
time: &ottl.StandardStringGetter[any]{
Getter: func(_ context.Context, _ any) (any, error) {
return "2023-05-26 12:34:56 HST", nil
},
},
format: "%Y-%m-%d %H:%M:%S %Z",
expected: time.Date(2023, 5, 26, 12, 34, 56, 0, time.FixedZone("HST", -10*60*60)),
},
{
name: "short date with timestamp without time zone offset",
time: &ottl.StandardStringGetter[any]{
Getter: func(_ context.Context, _ any) (any, error) {
return "2023-05-26T12:34:56 GMT", nil
},
},
format: "%Y-%m-%dT%H:%M:%S %Z",
expected: time.Date(2023, 5, 26, 12, 34, 56, 0, time.FixedZone("GMT", 0)),
},
{
name: "RFC 3339 in custom format",
time: &ottl.StandardStringGetter[any]{
Getter: func(_ context.Context, _ any) (any, error) {
return "2012-11-01T22:08:41+0000 EST", nil
},
},
format: "%Y-%m-%dT%H:%M:%S%z %Z",
expected: time.Date(2012, 11, 01, 22, 8, 41, 0, time.FixedZone("EST", 0)),
},
{
name: "RFC 3339 in custom format before 2000",
time: &ottl.StandardStringGetter[any]{
Getter: func(_ context.Context, _ any) (any, error) {
return "1986-10-01T00:17:33 MST", nil
},
},
format: "%Y-%m-%dT%H:%M:%S %Z",
expected: time.Date(1986, 10, 01, 00, 17, 33, 00, time.FixedZone("MST", -7*60*60)),
},
{
name: "no location",
time: &ottl.StandardStringGetter[any]{
Getter: func(_ context.Context, _ any) (any, error) {
return "2022/01/01", nil
},
},
format: "%Y/%m/%d",
expected: time.Date(2022, 01, 01, 0, 0, 0, 0, time.Local),
},
{
name: "with location - America",
time: &ottl.StandardStringGetter[any]{
Getter: func(_ context.Context, _ any) (any, error) {
return "2023-05-26 12:34:56", nil
},
},
format: "%Y-%m-%d %H:%M:%S",
location: "America/New_York",
expected: time.Date(2023, 5, 26, 12, 34, 56, 0, locationAmericaNewYork),
},
{
name: "with location - Asia",
time: &ottl.StandardStringGetter[any]{
Getter: func(_ context.Context, _ any) (any, error) {
return "2023-05-26 12:34:56", nil
},
},
format: "%Y-%m-%d %H:%M:%S",
location: "Asia/Shanghai",
expected: time.Date(2023, 5, 26, 12, 34, 56, 0, locationAsiaShanghai),
},
{
name: "RFC 3339 in custom format before 2000, ignore default location",
time: &ottl.StandardStringGetter[any]{
Getter: func(_ context.Context, _ any) (any, error) {
return "1986-10-01T00:17:33 MST", nil
},
},
location: "Asia/Shanghai",
format: "%Y-%m-%dT%H:%M:%S %Z",
expected: time.Date(1986, 10, 01, 00, 17, 33, 00, time.FixedZone("MST", -7*60*60)),
},
}
for _, tt := range tests {
var locOptional ottl.Optional[string]
if tt.location != "" {
locOptional = ottl.NewTestingOptional(tt.location)
}
exprFunc, err := Time(tt.time, tt.format, locOptional)
assert.NoError(t, err)

t.Run(tt.name, func(t *testing.B) {
result, err := exprFunc(nil, nil)
assert.NoError(t, err)
assert.Equal(t, tt.expected.UnixNano(), result.(time.Time).UnixNano())
})
}
}

0 comments on commit 7e8ebd7

Please sign in to comment.