From a81c8b3bf2697b86ac7d3d29d7c0fb71c0716a84 Mon Sep 17 00:00:00 2001 From: Koya IWAMURA Date: Wed, 24 Apr 2024 01:16:08 +0900 Subject: [PATCH 1/5] net/url: improve URL.String performance MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit URL.String performs memory allocation many times, but it can improve performance by allocating memory that it clearly knows it needs. This CL achieves 24.6% speedup, 18.3% memory reduction, and 46.7% fewer memory allocations on existing benchmarks. │ string_old.txt │ string_new2.txt │ │ sec/op │ sec/op vs base │ String-16 3.622µ ± 5% 2.730µ ± 2% -24.63% (p=0.000 n=10) │ string_old.txt │ string_new2.txt │ │ B/op │ B/op vs base │ String-16 1.406Ki ± 0% 1.148Ki ± 0% -18.33% (p=0.000 n=10) │ string_old.txt │ string_new2.txt │ │ allocs/op │ allocs/op vs base │ String-16 60.00 ± 0% 32.00 ± 0% -46.67% (p=0.000 n=10) Change-Id: I70199be952eddc44134945077e52740e8921088f Reviewed-on: https://go-review.googlesource.com/c/go/+/581155 Reviewed-by: Ian Lance Taylor LUCI-TryBot-Result: Go LUCI Auto-Submit: Ian Lance Taylor Reviewed-by: Emmanuel Odeke Reviewed-by: Joedian Reid --- src/net/url/url.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/net/url/url.go b/src/net/url/url.go index f362958edd..7cd6913ad7 100644 --- a/src/net/url/url.go +++ b/src/net/url/url.go @@ -814,6 +814,22 @@ func validOptionalPort(port string) bool { // - if u.Fragment is empty, #fragment is omitted. func (u *URL) String() string { var buf strings.Builder + + n := len(u.Scheme) + if u.Opaque != "" { + n += len(u.Opaque) + } else { + if !u.OmitHost && (u.Scheme != "" || u.Host != "" || u.User != nil) { + username := u.User.Username() + password, _ := u.User.Password() + n += len(username) + len(password) + len(u.Host) + } + n += len(u.Path) + } + n += len(u.RawQuery) + len(u.RawFragment) + n += len(":" + "//" + "//" + ":" + "@" + "/" + "./" + "?" + "#") + buf.Grow(n) + if u.Scheme != "" { buf.WriteString(u.Scheme) buf.WriteByte(':') From f6e6b637c0c5e66d177becd4b3539d54636f5705 Mon Sep 17 00:00:00 2001 From: plasmatium Date: Fri, 26 Apr 2024 01:15:34 +0000 Subject: [PATCH 2/5] time: add notes about monotonic time paused See #66870 Change-Id: I781265355a3dbd0d9538bc9dcafaa83b482ec3f8 GitHub-Last-Rev: 9d92f116b05db5568dd4d5834bace659eaf8cc49 GitHub-Pull-Request: golang/go#66922 Reviewed-on: https://go-review.googlesource.com/c/go/+/580515 Reviewed-by: Ian Lance Taylor Auto-Submit: Ian Lance Taylor Reviewed-by: Joedian Reid LUCI-TryBot-Result: Go LUCI --- src/time/time.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/time/time.go b/src/time/time.go index 8c24e1c481..0bbdeaecf5 100644 --- a/src/time/time.go +++ b/src/time/time.go @@ -53,7 +53,10 @@ // // On some systems the monotonic clock will stop if the computer goes to sleep. // On such a system, t.Sub(u) may not accurately reflect the actual -// time that passed between t and u. +// time that passed between t and u. The same applies to other functions and +// methods that subtract times, such as [Since], [Until], [Before], [After], +// [Add], [Sub], [Equal] and [Compare]. In some cases, you may need to strip +// the monotonic clock to get accurate results. // // Because the monotonic clock reading has no meaning outside // the current process, the serialized forms generated by t.GobEncode, From 9e0685f9c24ff80a7e710e764c1090c680a5095c Mon Sep 17 00:00:00 2001 From: aimuz Date: Sun, 28 Apr 2024 01:52:06 +0000 Subject: [PATCH 3/5] encoding/json: optimize field sorting with slices and cmp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactor field sorting in typeFields to use slices.SortFunc and cmp.Compare, resulting in performance improvements in cache misses and slight increases in hit latency. Memory allocation and operation counts also reduced significantly for cache misses. goos: darwin goarch: arm64 pkg: encoding/json cpu: Apple M2 Max │ old.txt │ new.txt │ │ sec/op │ sec/op vs base │ TypeFieldsCache/MissTypes1-12 5.068µ ± 2% 5.032µ ± 1% ~ (p=0.055 n=10) TypeFieldsCache/MissTypes10-12 15.22µ ± 2% 14.79µ ± 2% -2.84% (p=0.000 n=10) TypeFieldsCache/MissTypes100-12 98.40µ ± 3% 94.72µ ± 1% -3.73% (p=0.002 n=10) TypeFieldsCache/MissTypes1000-12 860.3µ ± 3% 840.7µ ± 1% ~ (p=0.105 n=10) TypeFieldsCache/MissTypes10000-12 8.092m ± 3% 7.987m ± 4% ~ (p=0.280 n=10) TypeFieldsCache/MissTypes100000-12 80.90m ± 9% 80.62m ± 2% ~ (p=0.631 n=10) TypeFieldsCache/MissTypes1000000-12 1009.5m ± 7% 933.8m ± 7% -7.50% (p=0.011 n=10) TypeFieldsCache/HitTypes1-12 1.554n ± 3% 1.617n ± 3% +4.05% (p=0.004 n=10) TypeFieldsCache/HitTypes10-12 1.575n ± 1% 1.624n ± 6% +3.14% (p=0.000 n=10) TypeFieldsCache/HitTypes100-12 1.566n ± 1% 1.627n ± 5% +3.86% (p=0.000 n=10) TypeFieldsCache/HitTypes1000-12 1.526n ± 4% 1.596n ± 3% +4.55% (p=0.001 n=10) TypeFieldsCache/HitTypes10000-12 1.506n ± 0% 1.573n ± 3% +4.41% (p=0.000 n=10) TypeFieldsCache/HitTypes100000-12 1.480n ± 1% 1.529n ± 7% +3.35% (p=0.001 n=10) TypeFieldsCache/HitTypes1000000-12 1.473n ± 2% 1.468n ± 2% ~ (p=0.739 n=10) geomean 1.371µ 1.374µ +0.25% │ old.txt │ new.txt │ │ B/op │ B/op vs base │ TypeFieldsCache/MissTypes1-12 2.023Ki ± 0% 2.000Ki ± 0% -1.16% (p=0.000 n=10) TypeFieldsCache/MissTypes10-12 10.95Ki ± 0% 10.71Ki ± 0% -2.17% (p=0.000 n=10) TypeFieldsCache/MissTypes100-12 101.91Ki ± 0% 99.53Ki ± 0% -2.34% (p=0.000 n=10) TypeFieldsCache/MissTypes1000-12 1044.3Ki ± 0% 1020.8Ki ± 0% -2.25% (p=0.000 n=10) TypeFieldsCache/MissTypes10000-12 9.957Mi ± 0% 9.730Mi ± 0% -2.29% (p=0.000 n=10) TypeFieldsCache/MissTypes100000-12 97.79Mi ± 0% 95.50Mi ± 0% -2.34% (p=0.000 n=10) TypeFieldsCache/MissTypes1000000-12 1018.4Mi ± 0% 995.5Mi ± 0% -2.25% (p=0.000 n=10) TypeFieldsCache/HitTypes1-12 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ TypeFieldsCache/HitTypes10-12 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ TypeFieldsCache/HitTypes100-12 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ TypeFieldsCache/HitTypes1000-12 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ TypeFieldsCache/HitTypes10000-12 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ TypeFieldsCache/HitTypes100000-12 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ TypeFieldsCache/HitTypes1000000-12 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ geomean ² -1.06% ² ¹ all samples are equal ² summaries must be >0 to compute geomean │ old.txt │ new.txt │ │ allocs/op │ allocs/op vs base │ TypeFieldsCache/MissTypes1-12 46.00 ± 0% 45.00 ± 0% -2.17% (p=0.000 n=10) TypeFieldsCache/MissTypes10-12 215.0 ± 0% 205.0 ± 0% -4.65% (p=0.000 n=10) TypeFieldsCache/MissTypes100-12 1.845k ± 0% 1.745k ± 0% -5.42% (p=0.000 n=10) TypeFieldsCache/MissTypes1000-12 18.08k ± 0% 17.08k ± 0% -5.53% (p=0.000 n=10) TypeFieldsCache/MissTypes10000-12 180.3k ± 0% 170.3k ± 0% -5.55% (p=0.000 n=10) TypeFieldsCache/MissTypes100000-12 1.804M ± 0% 1.704M ± 0% -5.54% (p=0.000 n=10) TypeFieldsCache/MissTypes1000000-12 18.04M ± 0% 17.04M ± 0% -5.54% (p=0.000 n=10) TypeFieldsCache/HitTypes1-12 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ TypeFieldsCache/HitTypes10-12 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ TypeFieldsCache/HitTypes100-12 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ TypeFieldsCache/HitTypes1000-12 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ TypeFieldsCache/HitTypes10000-12 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ TypeFieldsCache/HitTypes100000-12 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ TypeFieldsCache/HitTypes1000000-12 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ geomean ² -2.49% ² ¹ all samples are equal ² summaries must be >0 to compute geomean Change-Id: I62b8f524f5747f4f1b9241a1d849efeef1851049 GitHub-Last-Rev: d16772d4fb44f76e2688e15f1bdfc7db14bb55c6 GitHub-Pull-Request: golang/go#67046 Reviewed-on: https://go-review.googlesource.com/c/go/+/581895 LUCI-TryBot-Result: Go LUCI Auto-Submit: Ian Lance Taylor Reviewed-by: Ian Lance Taylor Reviewed-by: Joedian Reid --- src/encoding/json/encode.go | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/encoding/json/encode.go b/src/encoding/json/encode.go index 0035a65cfc..bd55c7caf0 100644 --- a/src/encoding/json/encode.go +++ b/src/encoding/json/encode.go @@ -12,13 +12,13 @@ package json import ( "bytes" + "cmp" "encoding" "encoding/base64" "fmt" "math" "reflect" "slices" - "sort" "strconv" "strings" "sync" @@ -1162,21 +1162,23 @@ func typeFields(t reflect.Type) structFields { } } - sort.Slice(fields, func(i, j int) bool { - x := fields + slices.SortFunc(fields, func(a, b field) int { // sort field by name, breaking ties with depth, then // breaking ties with "name came from json tag", then // breaking ties with index sequence. - if x[i].name != x[j].name { - return x[i].name < x[j].name + if c := strings.Compare(a.name, b.name); c != 0 { + return c } - if len(x[i].index) != len(x[j].index) { - return len(x[i].index) < len(x[j].index) + if c := cmp.Compare(len(a.index), len(b.index)); c != 0 { + return c } - if x[i].tag != x[j].tag { - return x[i].tag + if a.tag != b.tag { + if a.tag { + return -1 + } + return +1 } - return slices.Compare(x[i].index, x[j].index) == -1 + return slices.Compare(a.index, b.index) }) // Delete all fields that are hidden by the Go rules for embedded fields, From cf164403d1ae1459bf48cb7aea3cf16f8aca5a98 Mon Sep 17 00:00:00 2001 From: Jes Cok Date: Fri, 26 Apr 2024 21:49:41 +0800 Subject: [PATCH 4/5] slices: reduce code nesting depth for Compact and CompactFunc To make it easier to read. Change-Id: I2fa1eb78d879b9d86b4dc839be7ede37c7c864f4 Reviewed-on: https://go-review.googlesource.com/c/go/+/581976 Auto-Submit: Ian Lance Taylor LUCI-TryBot-Result: Go LUCI Reviewed-by: Ian Lance Taylor Reviewed-by: Joedian Reid --- src/slices/slices.go | 50 +++++++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/src/slices/slices.go b/src/slices/slices.go index ae4c2adbf4..857ab46314 100644 --- a/src/slices/slices.go +++ b/src/slices/slices.go @@ -355,20 +355,21 @@ func Clone[S ~[]E, E any](s S) S { // which may have a smaller length. // Compact zeroes the elements between the new length and the original length. func Compact[S ~[]E, E comparable](s S) S { - if len(s) > 1 { - for k := 1; k < len(s); k++ { - if s[k] == s[k-1] { - s2 := s[k:] - for k2 := 1; k2 < len(s2); k2++ { - if s2[k2] != s2[k2-1] { - s[k] = s2[k2] - k++ - } + if len(s) < 2 { + return s + } + for k := 1; k < len(s); k++ { + if s[k] == s[k-1] { + s2 := s[k:] + for k2 := 1; k2 < len(s2); k2++ { + if s2[k2] != s2[k2-1] { + s[k] = s2[k2] + k++ } - - clear(s[k:]) // zero/nil out the obsolete elements, for GC - return s[:k] } + + clear(s[k:]) // zero/nil out the obsolete elements, for GC + return s[:k] } } return s @@ -378,20 +379,21 @@ func Compact[S ~[]E, E comparable](s S) S { // For runs of elements that compare equal, CompactFunc keeps the first one. // CompactFunc zeroes the elements between the new length and the original length. func CompactFunc[S ~[]E, E any](s S, eq func(E, E) bool) S { - if len(s) > 1 { - for k := 1; k < len(s); k++ { - if eq(s[k], s[k-1]) { - s2 := s[k:] - for k2 := 1; k2 < len(s2); k2++ { - if !eq(s2[k2], s2[k2-1]) { - s[k] = s2[k2] - k++ - } + if len(s) < 2 { + return s + } + for k := 1; k < len(s); k++ { + if eq(s[k], s[k-1]) { + s2 := s[k:] + for k2 := 1; k2 < len(s2); k2++ { + if !eq(s2[k2], s2[k2-1]) { + s[k] = s2[k2] + k++ } - - clear(s[k:]) // zero/nil out the obsolete elements, for GC - return s[:k] } + + clear(s[k:]) // zero/nil out the obsolete elements, for GC + return s[:k] } } return s From 16ce8b3925deaeb72541ee96b6ee23a08fc21dea Mon Sep 17 00:00:00 2001 From: David du Colombier <0intro@gmail.com> Date: Sun, 28 Apr 2024 14:39:56 +0200 Subject: [PATCH 5/5] net: fix lookupHost on Plan 9 CL 532217 added the newDNSError function. However, the implementation was not correct on Plan 9, which lead TestLookupNoSuchHost to fail. This change fixes lookupHost on Plan 9. Fixes #67096. Change-Id: I39271f7d588b19c1b1608f18a24d871460be09cd Reviewed-on: https://go-review.googlesource.com/c/go/+/582236 Reviewed-by: Joedian Reid Run-TryBot: David du Colombier <0intro@gmail.com> LUCI-TryBot-Result: Go LUCI TryBot-Result: Gopher Robot Reviewed-by: Ian Lance Taylor --- src/net/lookup_plan9.go | 19 +++++-------------- src/net/lookup_test.go | 4 ++++ 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/src/net/lookup_plan9.go b/src/net/lookup_plan9.go index 2532a0e967..588174b1fc 100644 --- a/src/net/lookup_plan9.go +++ b/src/net/lookup_plan9.go @@ -109,17 +109,11 @@ func queryDNS(ctx context.Context, addr string, typ string) (res []string, err e func handlePlan9DNSError(err error, name string) error { if stringsHasSuffix(err.Error(), "dns: name does not exist") || stringsHasSuffix(err.Error(), "dns: resource does not exist; negrcode 0") || - stringsHasSuffix(err.Error(), "dns: resource does not exist; negrcode") { - return &DNSError{ - Err: errNoSuchHost.Error(), - Name: name, - IsNotFound: true, - } - } - return &DNSError{ - Err: err.Error(), - Name: name, + stringsHasSuffix(err.Error(), "dns: resource does not exist; negrcode") || + stringsHasSuffix(err.Error(), "dns failure") { + err = errNoSuchHost } + return newDNSError(err, name, "") } // toLower returns a lower-case version of in. Restricting us to @@ -169,10 +163,7 @@ func (*Resolver) lookupHost(ctx context.Context, host string) (addrs []string, e // host names in local network (e.g. from /lib/ndb/local) lines, err := queryCS(ctx, "net", host, "1") if err != nil { - if stringsHasSuffix(err.Error(), "dns failure") { - err = errNoSuchHost - } - return nil, newDNSError(err, host, "") + return nil, handlePlan9DNSError(err, host) } loop: for _, line := range lines { diff --git a/src/net/lookup_test.go b/src/net/lookup_test.go index bd58498fbc..97b37f2841 100644 --- a/src/net/lookup_test.go +++ b/src/net/lookup_test.go @@ -1634,6 +1634,10 @@ func TestLookupNoSuchHost(t *testing.T) { } func TestDNSErrorUnwrap(t *testing.T) { + if runtime.GOOS == "plan9" { + // The Plan 9 implementation of the resolver doesn't use the Dial function yet. See https://go.dev/cl/409234 + t.Skip("skipping on plan9") + } rDeadlineExcceeded := &Resolver{PreferGo: true, Dial: func(ctx context.Context, network, address string) (Conn, error) { return nil, context.DeadlineExceeded }}