mirror of
https://github.com/golang/go
synced 2024-11-24 05:20:04 -07:00
net: optimize WriteMsgUDPAddrPort
This is one step towards optimizing WriteMsgUDPAddrPort. Further steps remain, namely to avoid the syscall.Sockaddr interface, as we do for UDPConn.WriteToUDP and UDPConn.ReadFromUDP. A previous change optimized ReadMsgUDPAddrPort by having ReadMsgUDP call ReadMsgUDPAddrPort rather than the other way around. This change does not implement WriteMsgUDP in terms of WriteMsgUDPAddrPort, because a few layers deep, on posix platforms only (in ipToSockaddrInet4 and ipToSockaddrInet6), is special handling of zero-length IP addresses and IPv4zero. It treats IP(nil) as equivalent to 0.0.0.0 or ::, and 0.0.0.0 as equivalent to :: in an IPv6 context. Based on the comments, I suspect that this treatment was intended for the Listen* API, not the Write* API, but it affects both, and I am nervous about changing the behavior for Write*. The netip package doesn't have a way to represent a "zero-length IP address" as distinct from an invalid IP address (which is a good thing), so to implement WriteMsgUDP using WriteMsgUDPAddrPort, we would have to duplicate this special handling at the start of WriteMsgUDP. But this handling depends on whether the UDPConn is an IPv4 or an IPv6 conn, which is also platform-specific information. As a result, every attempt I made to implement WriteMsgUDP using WriteMsgUDPAddrPort while preserving behavior ended up being considerably worse than copy/paste/modify. This does mean that WriteMsgUDP and WriteMsgUDPAddrPort will have different behavior in these cases. name old time/op new time/op delta ReadWriteMsgUDPAddrPort-8 5.29µs ± 6% 5.02µs ± 7% -5.14% (p=0.000 n=13+15) name old alloc/op new alloc/op delta ReadWriteMsgUDPAddrPort-8 128B ± 0% 64B ± 0% -50.00% (p=0.000 n=15+15) name old allocs/op new allocs/op delta ReadWriteMsgUDPAddrPort-8 4.00 ± 0% 2.00 ± 0% -50.00% (p=0.000 n=15+15) Change-Id: Ia78eb49734f4301d7772dfdbb5a87e4d303a9f7a Reviewed-on: https://go-review.googlesource.com/c/go/+/360597 Trust: Josh Bleecher Snyder <josharian@gmail.com> Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
This commit is contained in:
parent
3c61cb3dcd
commit
6f1e9a9c21
@ -9,6 +9,7 @@ package net
|
||||
import (
|
||||
"context"
|
||||
"internal/poll"
|
||||
"net/netip"
|
||||
"runtime"
|
||||
"syscall"
|
||||
)
|
||||
@ -196,3 +197,32 @@ func ipToSockaddr(family int, ip IP, port int, zone string) (syscall.Sockaddr, e
|
||||
}
|
||||
return nil, &AddrError{Err: "invalid address family", Addr: ip.String()}
|
||||
}
|
||||
|
||||
func addrPortToSockaddrInet4(ap netip.AddrPort) (syscall.SockaddrInet4, error) {
|
||||
// ipToSockaddrInet4 has special handling here for zero length slices.
|
||||
// We do not, because netip has no concept of a generic zero IP address.
|
||||
addr := ap.Addr()
|
||||
if !addr.Is4() {
|
||||
return syscall.SockaddrInet4{}, &AddrError{Err: "non-IPv4 address", Addr: addr.String()}
|
||||
}
|
||||
sa := syscall.SockaddrInet4{
|
||||
Addr: addr.As4(),
|
||||
Port: int(ap.Port()),
|
||||
}
|
||||
return sa, nil
|
||||
}
|
||||
|
||||
func addrPortToSockaddrInet6(ap netip.AddrPort) (syscall.SockaddrInet6, error) {
|
||||
// ipToSockaddrInet6 has special handling here for zero length slices.
|
||||
// We do not, because netip has no concept of a generic zero IP address.
|
||||
addr := ap.Addr()
|
||||
if !addr.Is6() {
|
||||
return syscall.SockaddrInet6{}, &AddrError{Err: "non-IPv6 address", Addr: addr.String()}
|
||||
}
|
||||
sa := syscall.SockaddrInet6{
|
||||
Addr: addr.As16(),
|
||||
Port: int(ap.Port()),
|
||||
ZoneId: uint32(zoneCache.index(addr.Zone())),
|
||||
}
|
||||
return sa, nil
|
||||
}
|
||||
|
@ -114,6 +114,13 @@ func UDPAddrFromAddrPort(addr netip.AddrPort) *UDPAddr {
|
||||
}
|
||||
}
|
||||
|
||||
// An addrPortUDPAddr is a netip.AddrPort-based UDP address that satisfies the Addr interface.
|
||||
type addrPortUDPAddr struct {
|
||||
netip.AddrPort
|
||||
}
|
||||
|
||||
func (addrPortUDPAddr) Network() string { return "udp" }
|
||||
|
||||
// UDPConn is the implementation of the Conn and PacketConn interfaces
|
||||
// for UDP network connections.
|
||||
type UDPConn struct {
|
||||
@ -244,11 +251,14 @@ func (c *UDPConn) WriteMsgUDP(b, oob []byte, addr *UDPAddr) (n, oobn int, err er
|
||||
|
||||
// WriteMsgUDPAddrPort is like WriteMsgUDP but takes a netip.AddrPort instead of a UDPAddr.
|
||||
func (c *UDPConn) WriteMsgUDPAddrPort(b, oob []byte, addr netip.AddrPort) (n, oobn int, err error) {
|
||||
// TODO(bradfitz): make this efficient, making the internal net package
|
||||
// type throughout be netip.Addr and only converting to the net.IP slice
|
||||
// version at the edge. But for now (2021-10-20), this is a wrapper around
|
||||
// the old way.
|
||||
return c.WriteMsgUDP(b, oob, UDPAddrFromAddrPort(addr))
|
||||
if !c.ok() {
|
||||
return 0, 0, syscall.EINVAL
|
||||
}
|
||||
n, oobn, err = c.writeMsgAddrPort(b, oob, addr)
|
||||
if err != nil {
|
||||
err = &OpError{Op: "write", Net: c.fd.net, Source: c.fd.laddr, Addr: addrPortUDPAddr{addr}, Err: err}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func newUDPConn(fd *netFD) *UDPConn { return &UDPConn{conn{fd}} }
|
||||
|
@ -57,6 +57,10 @@ func (c *UDPConn) writeMsg(b, oob []byte, addr *UDPAddr) (n, oobn int, err error
|
||||
return 0, 0, syscall.EPLAN9
|
||||
}
|
||||
|
||||
func (c *UDPConn) writeMsgAddrPort(b, oob []byte, addr netip.AddrPort) (n, oobn int, err error) {
|
||||
return 0, 0, syscall.EPLAN9
|
||||
}
|
||||
|
||||
func (sd *sysDialer) dialUDP(ctx context.Context, laddr, raddr *UDPAddr) (*UDPConn, error) {
|
||||
fd, err := dialPlan9(ctx, sd.network, laddr, raddr)
|
||||
if err != nil {
|
||||
|
@ -123,6 +123,34 @@ func (c *UDPConn) writeMsg(b, oob []byte, addr *UDPAddr) (n, oobn int, err error
|
||||
return c.fd.writeMsg(b, oob, sa)
|
||||
}
|
||||
|
||||
func (c *UDPConn) writeMsgAddrPort(b, oob []byte, addr netip.AddrPort) (n, oobn int, err error) {
|
||||
if c.fd.isConnected && addr.IsValid() {
|
||||
return 0, 0, ErrWriteToConnected
|
||||
}
|
||||
if !c.fd.isConnected && !addr.IsValid() {
|
||||
return 0, 0, errMissingAddress
|
||||
}
|
||||
|
||||
switch c.fd.family {
|
||||
case syscall.AF_INET:
|
||||
sa, err := addrPortToSockaddrInet4(addr)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
// TODO: Implement writeMsgInet4 to avoid allocation converting sa to an interface.
|
||||
return c.fd.writeMsg(b, oob, &sa)
|
||||
case syscall.AF_INET6:
|
||||
sa, err := addrPortToSockaddrInet6(addr)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
// TODO: Implement writeMsgInet6 to avoid allocation converting sa to an interface.
|
||||
return c.fd.writeMsg(b, oob, &sa)
|
||||
default:
|
||||
return 0, 0, &AddrError{Err: "invalid address family", Addr: addr.Addr().String()}
|
||||
}
|
||||
}
|
||||
|
||||
func (sd *sysDialer) dialUDP(ctx context.Context, laddr, raddr *UDPAddr) (*UDPConn, error) {
|
||||
fd, err := internetSocket(ctx, sd.network, laddr, raddr, syscall.SOCK_DGRAM, 0, "dial", sd.Dialer.Control)
|
||||
if err != nil {
|
||||
|
Loading…
Reference in New Issue
Block a user