mirror of
https://github.com/golang/go
synced 2024-11-19 02:04:42 -07:00
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>
This commit is contained in:
parent
19db745664
commit
1d214f7062
@ -4,7 +4,11 @@
|
||||
|
||||
package net
|
||||
|
||||
import "errors"
|
||||
import (
|
||||
"errors"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
errInvalidInterface = errors.New("invalid network interface")
|
||||
@ -88,9 +92,12 @@ func (ifi *Interface) MulticastAddrs() ([]Addr, error) {
|
||||
func Interfaces() ([]Interface, error) {
|
||||
ift, err := interfaceTable(0)
|
||||
if err != nil {
|
||||
err = &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: err}
|
||||
return nil, &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: err}
|
||||
}
|
||||
return ift, err
|
||||
if len(ift) != 0 {
|
||||
zoneCache.update(ift)
|
||||
}
|
||||
return ift, nil
|
||||
}
|
||||
|
||||
// InterfaceAddrs returns a list of the system's network interface
|
||||
@ -137,6 +144,9 @@ func InterfaceByName(name string) (*Interface, error) {
|
||||
if err != nil {
|
||||
return nil, &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: err}
|
||||
}
|
||||
if len(ift) != 0 {
|
||||
zoneCache.update(ift)
|
||||
}
|
||||
for _, ifi := range ift {
|
||||
if name == ifi.Name {
|
||||
return &ifi, nil
|
||||
@ -144,3 +154,68 @@ func InterfaceByName(name string) (*Interface, error) {
|
||||
}
|
||||
return nil, &OpError{Op: "route", Net: "ip+net", Source: nil, Addr: nil, Err: errNoSuchInterface}
|
||||
}
|
||||
|
||||
// An ipv6ZoneCache represents a cache holding partial network
|
||||
// interface information. It is used for reducing the cost of IPv6
|
||||
// addressing scope zone resolution.
|
||||
type ipv6ZoneCache struct {
|
||||
sync.RWMutex // guard the following
|
||||
lastFetched time.Time // last time routing information was fetched
|
||||
toIndex map[string]int // interface name to its index
|
||||
toName map[int]string // interface index to its name
|
||||
}
|
||||
|
||||
var zoneCache = ipv6ZoneCache{
|
||||
toIndex: make(map[string]int),
|
||||
toName: make(map[int]string),
|
||||
}
|
||||
|
||||
func (zc *ipv6ZoneCache) update(ift []Interface) {
|
||||
zc.Lock()
|
||||
defer zc.Unlock()
|
||||
now := time.Now()
|
||||
if zc.lastFetched.After(now.Add(-60 * time.Second)) {
|
||||
return
|
||||
}
|
||||
zc.lastFetched = now
|
||||
if len(ift) == 0 {
|
||||
var err error
|
||||
if ift, err = interfaceTable(0); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
zc.toIndex = make(map[string]int, len(ift))
|
||||
zc.toName = make(map[int]string, len(ift))
|
||||
for _, ifi := range ift {
|
||||
zc.toIndex[ifi.Name] = ifi.Index
|
||||
zc.toName[ifi.Index] = ifi.Name
|
||||
}
|
||||
}
|
||||
|
||||
func zoneToString(zone int) string {
|
||||
if zone == 0 {
|
||||
return ""
|
||||
}
|
||||
zoneCache.update(nil)
|
||||
zoneCache.RLock()
|
||||
defer zoneCache.RUnlock()
|
||||
name, ok := zoneCache.toName[zone]
|
||||
if !ok {
|
||||
name = uitoa(uint(zone))
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
func zoneToInt(zone string) int {
|
||||
if zone == "" {
|
||||
return 0
|
||||
}
|
||||
zoneCache.update(nil)
|
||||
zoneCache.RLock()
|
||||
defer zoneCache.RUnlock()
|
||||
index, ok := zoneCache.toIndex[zone]
|
||||
if !ok {
|
||||
index, _, _ = dtoi(zone, 0)
|
||||
}
|
||||
return index
|
||||
}
|
||||
|
@ -249,24 +249,3 @@ func internetAddrList(net, addr string, deadline time.Time) (addrList, error) {
|
||||
}
|
||||
return filterAddrList(filter, ips, inetaddr)
|
||||
}
|
||||
|
||||
func zoneToString(zone int) string {
|
||||
if zone == 0 {
|
||||
return ""
|
||||
}
|
||||
if ifi, err := InterfaceByIndex(zone); err == nil {
|
||||
return ifi.Name
|
||||
}
|
||||
return uitoa(uint(zone))
|
||||
}
|
||||
|
||||
func zoneToInt(zone string) int {
|
||||
if zone == "" {
|
||||
return 0
|
||||
}
|
||||
if ifi, err := InterfaceByName(zone); err == nil {
|
||||
return ifi.Index
|
||||
}
|
||||
n, _, _ := dtoi(zone, 0)
|
||||
return n
|
||||
}
|
||||
|
@ -12,6 +12,43 @@ import (
|
||||
"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
|
||||
|
Loading…
Reference in New Issue
Block a user