aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorVladimir Kuzmin <vkuzmin@uber.com>2018-03-07 19:46:54 -0800
committerBrad Fitzpatrick <bradfitz@golang.org>2018-03-09 17:03:05 +0000
commitb2d1cd2ad0dd1e0c84e6c3b2eccf77a5bcb84cdd (patch)
tree2475258b7168470d01c8551d1fb9bed5b031cf40 /src
parent5f541b11aaa345b4cf0fb37a80c32b704a6854ea (diff)
downloadgo-b2d1cd2ad0dd1e0c84e6c3b2eccf77a5bcb84cdd.tar.xz
net: optimize IP.String for IPv4
This is optimization is only for IPv4. It allocates a result buffer and writes the IPv4 octets as dotted decimal into it before converting it to a string just once, reducing allocations. Benchmark shows performance improvement: name old time/op new time/op delta IPString/IPv4-8 284ns ± 4% 144ns ± 6% -49.35% (p=0.000 n=19+17) IPString/IPv6-8 1.34µs ± 5% 1.14µs ± 5% -14.37% (p=0.000 n=19+20) name old alloc/op new alloc/op delta IPString/IPv4-8 24.0B ± 0% 16.0B ± 0% -33.33% (p=0.000 n=20+20) IPString/IPv6-8 232B ± 0% 224B ± 0% -3.45% (p=0.000 n=20+20) name old allocs/op new allocs/op delta IPString/IPv4-8 3.00 ± 0% 2.00 ± 0% -33.33% (p=0.000 n=20+20) IPString/IPv6-8 12.0 ± 0% 11.0 ± 0% -8.33% (p=0.000 n=20+20) Fixes #24306 Change-Id: I4e2d30d364e78183d55a42907d277744494b6df3 Reviewed-on: https://go-review.googlesource.com/99395 Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Diffstat (limited to 'src')
-rw-r--r--src/net/ip.go40
-rw-r--r--src/net/ip_test.go14
2 files changed, 49 insertions, 5 deletions
diff --git a/src/net/ip.go b/src/net/ip.go
index 6b7ba4c23e..a94ff73130 100644
--- a/src/net/ip.go
+++ b/src/net/ip.go
@@ -260,6 +260,25 @@ func (ip IP) Mask(mask IPMask) IP {
return out
}
+// ubtoa encodes the string form of the integer v to dst[start:] and
+// returns the number of bytes written to dst. The caller must ensure
+// that dst has sufficient length.
+func ubtoa(dst []byte, start int, v byte) int {
+ if v < 10 {
+ dst[start] = byte(v + '0')
+ return 1
+ } else if v < 100 {
+ dst[start+1] = byte(v%10 + '0')
+ dst[start] = byte(v/10 + '0')
+ return 2
+ }
+
+ dst[start+2] = byte(v%10 + '0')
+ dst[start+1] = byte((v/10)%10 + '0')
+ dst[start] = byte(v/100 + '0')
+ return 3
+}
+
// String returns the string form of the IP address ip.
// It returns one of 4 forms:
// - "<nil>", if ip has length 0
@@ -275,10 +294,23 @@ func (ip IP) String() string {
// If IPv4, use dotted notation.
if p4 := p.To4(); len(p4) == IPv4len {
- return uitoa(uint(p4[0])) + "." +
- uitoa(uint(p4[1])) + "." +
- uitoa(uint(p4[2])) + "." +
- uitoa(uint(p4[3]))
+ const maxIPv4StringLen = len("255.255.255.255")
+ b := make([]byte, maxIPv4StringLen)
+
+ n := ubtoa(b, 0, p4[0])
+ b[n] = '.'
+ n++
+
+ n += ubtoa(b, n, p4[1])
+ b[n] = '.'
+ n++
+
+ n += ubtoa(b, n, p4[2])
+ b[n] = '.'
+ n++
+
+ n += ubtoa(b, n, p4[3])
+ return string(b[:n])
}
if len(p) != IPv6len {
return "?" + hexString(ip)
diff --git a/src/net/ip_test.go b/src/net/ip_test.go
index ad13388dd2..60329e9cfe 100644
--- a/src/net/ip_test.go
+++ b/src/net/ip_test.go
@@ -252,9 +252,21 @@ var sink string
func BenchmarkIPString(b *testing.B) {
testHookUninstaller.Do(uninstallTestHooks)
+ b.Run("IPv4", func(b *testing.B) {
+ benchmarkIPString(b, IPv4len)
+ })
+
+ b.Run("IPv6", func(b *testing.B) {
+ benchmarkIPString(b, IPv6len)
+ })
+}
+
+func benchmarkIPString(b *testing.B, size int) {
+ b.ReportAllocs()
+ b.ResetTimer()
for i := 0; i < b.N; i++ {
for _, tt := range ipStringTests {
- if tt.in != nil {
+ if tt.in != nil && len(tt.in) == size {
sink = tt.in.String()
}
}