tstime: hand-implement parseInt for specific needs of rfc3339 parsing.

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 <danderson@tailscale.com>
This commit is contained in:
David Anderson 2020-04-14 12:05:19 -07:00 committed by Dave Anderson
parent dc9b39e3fb
commit b925e18f70
2 changed files with 44 additions and 4 deletions

View File

@ -8,7 +8,6 @@
import ( import (
"errors" "errors"
"fmt" "fmt"
"strconv"
"strings" "strings"
"sync" "sync"
"time" "time"
@ -105,10 +104,19 @@ func Parse3339(s string) (time.Time, error) {
} }
func parseInt(s string, dst *int) bool { func parseInt(s string, dst *int) bool {
n, err := strconv.ParseInt(s, 10, 0) if len(s) == 0 || len(s) > len("999999999") {
if err != nil || n < 0 { *dst = 0
return false 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 return true
} }

View File

@ -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) { func BenchmarkGoParse3339(b *testing.B) {
run := func(in string) func(*testing.B) { run := func(in string) func(*testing.B) {
return func(b *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.Fatalf("times don't stringify the same: %q vs %q", s1, s2)
} }
b.ResetTimer()
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
_, err := time.ParseInLocation(time.RFC3339Nano, in, loc) _, err := time.ParseInLocation(time.RFC3339Nano, in, loc)
if err != nil { if err != nil {
@ -153,3 +178,10 @@ func BenchmarkParse3339(b *testing.B) {
b.Run("Z", run("2020-04-05T15:56:00.148487491Z")) b.Run("Z", run("2020-04-05T15:56:00.148487491Z"))
b.Run("TZ", run("2020-04-05T15:56:00.148487491+08:00")) 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)
}
}