mirror of
https://github.com/golang/go
synced 2024-11-19 02:54: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
|
package net
|
||||||
|
|
||||||
import "errors"
|
import (
|
||||||
|
"errors"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
errInvalidInterface = errors.New("invalid network interface")
|
errInvalidInterface = errors.New("invalid network interface")
|
||||||
@ -88,9 +92,12 @@ func (ifi *Interface) MulticastAddrs() ([]Addr, error) {
|
|||||||
func Interfaces() ([]Interface, error) {
|
func Interfaces() ([]Interface, error) {
|
||||||
ift, err := interfaceTable(0)
|
ift, err := interfaceTable(0)
|
||||||
if err != nil {
|
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
|
// InterfaceAddrs returns a list of the system's network interface
|
||||||
@ -137,6 +144,9 @@ func InterfaceByName(name string) (*Interface, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, &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}
|
||||||
}
|
}
|
||||||
|
if len(ift) != 0 {
|
||||||
|
zoneCache.update(ift)
|
||||||
|
}
|
||||||
for _, ifi := range ift {
|
for _, ifi := range ift {
|
||||||
if name == ifi.Name {
|
if name == ifi.Name {
|
||||||
return &ifi, nil
|
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}
|
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)
|
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"
|
"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 {
|
type resolveUDPAddrTest struct {
|
||||||
network string
|
network string
|
||||||
litAddrOrName string
|
litAddrOrName string
|
||||||
|
Loading…
Reference in New Issue
Block a user