mirror of
https://github.com/tailscale/tailscale.git
synced 2024-11-25 19:15:34 +00:00
aa9d7f4665
Use go4.org/mem for memory safety. A slight performance hit, but a huge performance win for clients who start with a []byte. The perf hit is due largely to the MapHash call, which adds ~25ns. That is necessary to keep the fast path allocation-free. name old time/op new time/op delta GoParse3339/Z-8 281ns ± 1% 283ns ± 2% ~ (p=0.366 n=9+9) GoParse3339/TZ-8 509ns ± 0% 510ns ± 1% ~ (p=0.059 n=9+9) GoParse3339InLocation-8 330ns ± 1% 330ns ± 0% ~ (p=0.802 n=10+6) Parse3339/Z-8 69.3ns ± 1% 74.4ns ± 1% +7.45% (p=0.000 n=9+10) Parse3339/TZ-8 110ns ± 1% 140ns ± 3% +27.42% (p=0.000 n=9+10) ParseInt-8 8.20ns ± 1% 8.17ns ± 1% ~ (p=0.452 n=9+9) name old alloc/op new alloc/op delta GoParse3339/Z-8 0.00B 0.00B ~ (all equal) GoParse3339/TZ-8 160B ± 0% 160B ± 0% ~ (all equal) GoParse3339InLocation-8 0.00B 0.00B ~ (all equal) Parse3339/Z-8 0.00B 0.00B ~ (all equal) Parse3339/TZ-8 0.00B 0.00B ~ (all equal) name old allocs/op new allocs/op delta GoParse3339/Z-8 0.00 0.00 ~ (all equal) GoParse3339/TZ-8 3.00 ± 0% 3.00 ± 0% ~ (all equal) GoParse3339InLocation-8 0.00 0.00 ~ (all equal) Parse3339/Z-8 0.00 0.00 ~ (all equal) Parse3339/TZ-8 0.00 0.00 ~ (all equal) Signed-off-by: Josh Bleecher Snyder <josh@tailscale.com>
190 lines
4.5 KiB
Go
190 lines
4.5 KiB
Go
// Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package tstime
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
|
|
"go4.org/mem"
|
|
)
|
|
|
|
func TestParse3339(t *testing.T) {
|
|
tests := []string{
|
|
"2020-04-05T15:56:00Z",
|
|
"2020-04-05T15:56:00.1234Z",
|
|
"2020-04-05T15:56:00+08:00",
|
|
|
|
"2020-04-05T15:56:00.1+08:00",
|
|
"2020-04-05T15:56:00.12+08:00",
|
|
"2020-04-05T15:56:00.012+08:00",
|
|
"2020-04-05T15:56:00.0012+08:00",
|
|
"2020-04-05T15:56:00.148487491+08:00",
|
|
|
|
"2020x04-05T15:56:00.1234+08:00",
|
|
"2020-04x05T15:56:00.1234+08:00",
|
|
"2020-04-05x15:56:00.1234+08:00",
|
|
"2020-04-05T15x56:00.1234+08:00",
|
|
"2020-04-05T15:56x00.1234+08:00",
|
|
"2020-04-05T15:56:00x1234+08:00",
|
|
}
|
|
for _, s := range tests {
|
|
t.Run(s, func(t *testing.T) {
|
|
goTime, goErr := time.Parse(time.RFC3339Nano, s)
|
|
|
|
Parse3339(s) // prime the tz cache so next parse use fast path
|
|
got, err := Parse3339(s)
|
|
|
|
if (err == nil) != (goErr == nil) {
|
|
t.Fatalf("for %q, go err = %v; our err = %v", s, goErr, err)
|
|
}
|
|
if err != nil {
|
|
return
|
|
}
|
|
if !goTime.Equal(got) {
|
|
t.Errorf("for %q, times not Equal: we got %v, want go's %v", s, got, goTime)
|
|
}
|
|
if goStr, ourStr := goTime.Format(time.RFC3339Nano), got.Format(time.RFC3339Nano); goStr != ourStr {
|
|
t.Errorf("for %q, strings not equal: we got %q, want go's %q", s, ourStr, goStr)
|
|
}
|
|
|
|
})
|
|
}
|
|
|
|
}
|
|
|
|
func TestZoneOf(t *testing.T) {
|
|
tests := []struct {
|
|
in, want string
|
|
}{
|
|
{"2020-04-05T15:56:00+08:00", "+08:00"},
|
|
{"2020-04-05T15:56:00-08:00", "-08:00"},
|
|
{"2020-04-05T15:56:00.12345-08:00", "-08:00"},
|
|
{"2020-04-05T15:56:00.12345-08:00", "-08:00"},
|
|
{"2020-04-05T15:56:00.12345-08:30", "-08:30"},
|
|
{"2020-04-05T15:56:00.12345-08:15", "-08:15"},
|
|
{"2020-04-05T15:56:00.12345-08:17", ""}, // don't cache weird offsets
|
|
{"2020-04-05T15:56:00.12345Z", "Z"},
|
|
{"2020-04-05T15:56:00Z", "Z"},
|
|
{"123+08:00", ""}, // too short
|
|
{"+08:00", ""}, // too short
|
|
}
|
|
for _, tt := range tests {
|
|
if got := zoneOf(mem.S(tt.in)); !got.EqualString(tt.want) {
|
|
t.Errorf("zoneOf(%q) = %q; want %q", tt.in, got.StringCopy(), tt.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
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(mem.S(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) {
|
|
b.ReportAllocs()
|
|
for i := 0; i < b.N; i++ {
|
|
_, err := time.Parse(time.RFC3339Nano, in)
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
b.Run("Z", run("2020-04-05T15:56:00.148487491Z"))
|
|
b.Run("TZ", run("2020-04-05T15:56:00.148487491+08:00"))
|
|
}
|
|
|
|
func BenchmarkGoParse3339InLocation(b *testing.B) {
|
|
b.ReportAllocs()
|
|
const in = `2020-04-05T15:56:00.148487491+08:00`
|
|
|
|
t, err := time.Parse(time.RFC3339Nano, in)
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
loc := t.Location()
|
|
|
|
t2, err := time.ParseInLocation(time.RFC3339Nano, in, loc)
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
if !t.Equal(t2) {
|
|
b.Fatal("not equal")
|
|
}
|
|
if s1, s2 := t.Format(time.RFC3339Nano), t2.Format(time.RFC3339Nano); s1 != s2 {
|
|
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 {
|
|
b.Fatal(err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkParse3339(b *testing.B) {
|
|
run := func(in string) func(*testing.B) {
|
|
return func(b *testing.B) {
|
|
b.ReportAllocs()
|
|
|
|
t, err := time.Parse(time.RFC3339Nano, in)
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
|
|
t2, err := Parse3339(in)
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
if !t.Equal(t2) {
|
|
b.Fatal("not equal")
|
|
}
|
|
if s1, s2 := t.Format(time.RFC3339Nano), t2.Format(time.RFC3339Nano); s1 != s2 {
|
|
b.Fatalf("times don't stringify the same: %q vs %q", s1, s2)
|
|
}
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
_, err := Parse3339(in)
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
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(mem.S("148487491"), &out)
|
|
}
|
|
}
|