From b925e18f7060238689c69b0f405792961983236c Mon Sep 17 00:00:00 2001 From: David Anderson Date: Tue, 14 Apr 2020 12:05:19 -0700 Subject: [PATCH] tstime: hand-implement parseInt for specific needs of rfc3339 parsing. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Makes parsing 4.6x faster. name old time/op new time/op delta ParseInt-12 32.1ns ± 1% 6.9ns ± 2% -78.55% (p=0.000 n=10+9) Signed-off-by: David Anderson --- tstime/tstime.go | 16 ++++++++++++---- tstime/tstime_test.go | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/tstime/tstime.go b/tstime/tstime.go index 93ad3ef52..ff515d535 100644 --- a/tstime/tstime.go +++ b/tstime/tstime.go @@ -8,7 +8,6 @@ package tstime import ( "errors" "fmt" - "strconv" "strings" "sync" "time" @@ -105,10 +104,19 @@ func Parse3339(s string) (time.Time, error) { } func parseInt(s string, dst *int) bool { - n, err := strconv.ParseInt(s, 10, 0) - if err != nil || n < 0 { + if len(s) == 0 || len(s) > len("999999999") { + *dst = 0 return false } - *dst = int(n) + n := 0 + for i := 0; i < len(s); i++ { + d := s[i] - '0' + if d > 9 { + *dst = 0 + return false + } + n = n*10 + int(d) + } + *dst = n return true } diff --git a/tstime/tstime_test.go b/tstime/tstime_test.go index 99c5b5441..7f46576e8 100644 --- a/tstime/tstime_test.go +++ b/tstime/tstime_test.go @@ -76,6 +76,30 @@ func TestZoneOf(t *testing.T) { } } +func TestParseInt(t *testing.T) { + tests := []struct { + in string + want int + ret bool + }{ + {"", 0, false}, + {"1", 1, true}, + {"999999999", 999999999, true}, + {"123456789", 123456789, true}, + {"000000", 0, true}, + {"bork", 0, false}, + {"123bork", 0, false}, + } + + for _, tt := range tests { + var got int + gotRet := parseInt(tt.in, &got) + if gotRet != tt.ret || got != tt.want { + t.Errorf("parseInt(%q) = %v, %d; want %v, %d", tt.in, gotRet, got, tt.ret, tt.want) + } + } +} + func BenchmarkGoParse3339(b *testing.B) { run := func(in string) func(*testing.B) { return func(b *testing.B) { @@ -113,6 +137,7 @@ func BenchmarkGoParse3339InLocation(b *testing.B) { b.Fatalf("times don't stringify the same: %q vs %q", s1, s2) } + b.ResetTimer() for i := 0; i < b.N; i++ { _, err := time.ParseInLocation(time.RFC3339Nano, in, loc) if err != nil { @@ -153,3 +178,10 @@ func BenchmarkParse3339(b *testing.B) { b.Run("Z", run("2020-04-05T15:56:00.148487491Z")) b.Run("TZ", run("2020-04-05T15:56:00.148487491+08:00")) } + +func BenchmarkParseInt(b *testing.B) { + var out int + for i := 0; i < b.N; i++ { + parseInt("148487491", &out) + } +}