net: cache IPv6 zone information for applications using IPv6 link-local address

This change reduces the overhead of calling routing information per IPv6
link-local datagram read by caching IPv6 addressing scope zone
information.

Fixes #15237.

name                    old time/op    new time/op    delta
UDP6LinkLocalUnicast-8    64.9µs ± 0%    18.6µs ± 0%  -71.30%

name                    old alloc/op   new alloc/op   delta
UDP6LinkLocalUnicast-8    11.2kB ± 0%     0.2kB ± 0%  -98.42%

name                    old allocs/op  new allocs/op  delta
UDP6LinkLocalUnicast-8       101 ± 0%         3 ± 0%  -97.03%

Change-Id: I5ae2ef5058df1028bbb7f4ab32b13edfb330c3a7
Reviewed-on: https://go-review.googlesource.com/21952
Reviewed-by: Ian Lance Taylor <iant@golang.org>
diff --git a/src/net/udpsock_test.go b/src/net/udpsock_test.go
index 1da24b2..29d769c 100644
--- a/src/net/udpsock_test.go
+++ b/src/net/udpsock_test.go
@@ -12,6 +12,43 @@
 	"time"
 )
 
+func BenchmarkUDP6LinkLocalUnicast(b *testing.B) {
+	testHookUninstaller.Do(uninstallTestHooks)
+
+	if !supportsIPv6 {
+		b.Skip("IPv6 is not supported")
+	}
+	ifi := loopbackInterface()
+	if ifi == nil {
+		b.Skip("loopback interface not found")
+	}
+	lla := ipv6LinkLocalUnicastAddr(ifi)
+	if lla == "" {
+		b.Skip("IPv6 link-local unicast address not found")
+	}
+
+	c1, err := ListenPacket("udp6", JoinHostPort(lla+"%"+ifi.Name, "0"))
+	if err != nil {
+		b.Fatal(err)
+	}
+	defer c1.Close()
+	c2, err := ListenPacket("udp6", JoinHostPort(lla+"%"+ifi.Name, "0"))
+	if err != nil {
+		b.Fatal(err)
+	}
+	defer c2.Close()
+
+	var buf [1]byte
+	for i := 0; i < b.N; i++ {
+		if _, err := c1.WriteTo(buf[:], c2.LocalAddr()); err != nil {
+			b.Fatal(err)
+		}
+		if _, _, err := c2.ReadFrom(buf[:]); err != nil {
+			b.Fatal(err)
+		}
+	}
+}
+
 type resolveUDPAddrTest struct {
 	network       string
 	litAddrOrName string