From 1c529561889eebf400546fe3d5bec91e5b998c9a Mon Sep 17 00:00:00 2001 From: Julien Cretel Date: Mon, 13 Oct 2025 16:28:31 +0200 Subject: [PATCH 1/2] net/url: add benchmarks for Encode --- src/net/url/url_test.go | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/net/url/url_test.go b/src/net/url/url_test.go index 6084facacc0519..501558403ac771 100644 --- a/src/net/url/url_test.go +++ b/src/net/url/url_test.go @@ -1108,6 +1108,17 @@ var encodeQueryTests = []EncodeQueryTest{ "b": {"b1", "b2", "b3"}, "c": {"c1", "c2", "c3"}, }, "a=a1&a=a2&a=a3&b=b1&b=b2&b=b3&c=c1&c=c2&c=c3"}, + {Values{ + "a": {"a"}, + "b": {"b"}, + "c": {"c"}, + "d": {"d"}, + "e": {"e"}, + "f": {"f"}, + "g": {"g"}, + "h": {"h"}, + "i": {"i"}, + }, "a=a&b=b&c=c&d=d&e=e&f=f&g=g&h=h&i=i"}, } func TestEncodeQuery(t *testing.T) { @@ -1118,6 +1129,17 @@ func TestEncodeQuery(t *testing.T) { } } +func BenchmarkEncodeQuery(b *testing.B) { + for _, tt := range encodeQueryTests { + b.Run(tt.expected, func(b *testing.B) { + b.ReportAllocs() + for b.Loop() { + tt.m.Encode() + } + }) + } +} + var resolvePathTests = []struct { base, ref, expected string }{ From f6442a69d3e8c284ae94f0aba30bb144bd5cc718 Mon Sep 17 00:00:00 2001 From: Julien Cretel Date: Mon, 13 Oct 2025 16:28:41 +0200 Subject: [PATCH 2/2] net/url: eschew iterators in Encode --- src/net/url/url.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/net/url/url.go b/src/net/url/url.go index 6afa30f162bd25..4508f266084036 100644 --- a/src/net/url/url.go +++ b/src/net/url/url.go @@ -15,7 +15,6 @@ package url import ( "errors" "fmt" - "maps" "net/netip" "path" "slices" @@ -1046,7 +1045,16 @@ func (v Values) Encode() string { return "" } var buf strings.Builder - for _, k := range slices.Sorted(maps.Keys(v)) { + // To minimize allocations, we eschew iterators and pre-size the slice in + // which we collect v's keys. + keys := make([]string, len(v)) + var i int + for k := range v { + keys[i] = k + i++ + } + slices.Sort(keys) + for _, k := range keys { vs := v[k] keyEscaped := QueryEscape(k) for _, v := range vs {