From 133c0e9011b8c0e13442cdb754d7f6a8ac54ce00 Mon Sep 17 00:00:00 2001 From: Amarjeet Anand Date: Sun, 7 Aug 2022 23:49:19 +0530 Subject: [PATCH] time: optimize Format for RFC3339 and RFC3339Nano MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Optimise Format for the most frequently used RFC3339 and RFC3339Nano layouts by avoiding parsing of layout. > benchstat oldBench.txt newBench.txt name old time/op new time/op delta FormatRFC3339-8 302ns ± 1% 203ns ± 0% -32.89% (p=0.016 n=5+4) FormatRFC3339Nano-8 337ns ± 1% 219ns ± 1% -34.91% (p=0.008 n=5+5) name old alloc/op new alloc/op delta FormatRFC3339-8 32.0B ± 0% 32.0B ± 0% ~ (all equal) FormatRFC3339Nano-8 32.0B ± 0% 32.0B ± 0% ~ (all equal) name old allocs/op new allocs/op delta FormatRFC3339-8 1.00 ± 0% 1.00 ± 0% ~ (all equal) FormatRFC3339Nano-8 1.00 ± 0% 1.00 ± 0% ~ (all equal) Fixes #54093 Change-Id: Ifc84fce6078e24514ecbcd234875bca4aaab5e0e Reviewed-on: https://go-review.googlesource.com/c/go/+/421877 Run-TryBot: Ian Lance Taylor Auto-Submit: Joseph Tsai TryBot-Result: Gopher Robot Auto-Submit: Ian Lance Taylor Reviewed-by: Than McIntosh Reviewed-by: Ian Lance Taylor Reviewed-by: Joseph Tsai Run-TryBot: Joseph Tsai --- src/time/format.go | 51 +++++++++++++++++++++++++++++++++++++++++++ src/time/time_test.go | 14 ++++++++++++ 2 files changed, 65 insertions(+) diff --git a/src/time/format.go b/src/time/format.go index 61a9eb3301..721a207c0e 100644 --- a/src/time/format.go +++ b/src/time/format.go @@ -626,6 +626,15 @@ func (t Time) AppendFormat(b []byte, layout string) []byte { min int sec int ) + + // Handle most frequent layouts separately. + switch layout { + case RFC3339: + return t.appendFormatRFC3339(b, abs, offset, false) + case RFC3339Nano: + return t.appendFormatRFC3339(b, abs, offset, true) + } + // Each iteration generates one std value. for layout != "" { prefix, std, suffix := nextStdChunk(layout) @@ -781,6 +790,48 @@ func (t Time) AppendFormat(b []byte, layout string) []byte { return b } +func (t Time) appendFormatRFC3339(b []byte, abs uint64, offset int, nanos bool) []byte { + // Format date. + year, month, day, _ := absDate(abs, true) + b = appendInt(b, year, 4) + b = append(b, '-') + b = appendInt(b, int(month), 2) + b = append(b, '-') + b = appendInt(b, day, 2) + + b = append(b, 'T') + + // Format time. + hour, min, sec := absClock(abs) + b = appendInt(b, hour, 2) + b = append(b, ':') + b = appendInt(b, min, 2) + b = append(b, ':') + b = appendInt(b, sec, 2) + + if nanos { + std := stdFracSecond(stdFracSecond9, 9, '.') + b = formatNano(b, uint(t.Nanosecond()), std) + } + + if offset == 0 { + return append(b, 'Z') + } + + // Format zone. + zone := offset / 60 // convert to minutes + if zone < 0 { + b = append(b, '-') + zone = -zone + } else { + b = append(b, '+') + } + b = appendInt(b, zone/60, 2) + b = append(b, ':') + b = appendInt(b, zone%60, 2) + return b +} + var errBad = errors.New("bad value for field") // placeholder not passed to user // ParseError describes a problem parsing a time string. diff --git a/src/time/time_test.go b/src/time/time_test.go index 6fde5f6470..059c71bb8a 100644 --- a/src/time/time_test.go +++ b/src/time/time_test.go @@ -1402,6 +1402,20 @@ func BenchmarkFormat(b *testing.B) { } } +func BenchmarkFormatRFC3339(b *testing.B) { + t := Unix(1265346057, 0) + for i := 0; i < b.N; i++ { + t.Format("2006-01-02T15:04:05Z07:00") + } +} + +func BenchmarkFormatRFC3339Nano(b *testing.B) { + t := Unix(1265346057, 0) + for i := 0; i < b.N; i++ { + t.Format("2006-01-02T15:04:05.999999999Z07:00") + } +} + func BenchmarkFormatNow(b *testing.B) { // Like BenchmarkFormat, but easier, because the time zone // lookup cache is optimized for the present. -- 2.48.1