Skip to content

Commit

Permalink
net: add ReadFromUDPAddrPort
Browse files Browse the repository at this point in the history
It is now possible to do completely allocation-free UDP.

This is implemented completely separately from ReadFromUDP
because ReadFromUDP exists in a delicate balance to allow
mid-stack inlining. After performance-sensitive callers have
migrated to ReadFromUDPAddrPort, we may be able to simplify
ReadFromUDP to call ReadFromUDPAddrPort.

name                          old time/op    new time/op    delta
WriteToReadFromUDPAddrPort-8    4.71µs ± 2%    4.81µs ± 5%    +2.18%  (p=0.000 n=14+14)

name                          old alloc/op   new alloc/op   delta
WriteToReadFromUDPAddrPort-8     4.00B ± 0%     0.00B       -100.00%  (p=0.000 n=15+15)

name                          old allocs/op  new allocs/op  delta
WriteToReadFromUDPAddrPort-8      1.00 ± 0%      0.00       -100.00%  (p=0.000 n=15+15)

Change-Id: I37f5ad9416a1d4333ed48d83474b2cf933b2a1be
Reviewed-on: https://go-review.googlesource.com/c/go/+/360600
Trust: Josh Bleecher Snyder <[email protected]>
Run-TryBot: Josh Bleecher Snyder <[email protected]>
TryBot-Result: Go Bot <[email protected]>
Reviewed-by: Brad Fitzpatrick <[email protected]>
  • Loading branch information
josharian committed Nov 2, 2021
1 parent 433ba58 commit c702f91
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 1 deletion.
12 changes: 12 additions & 0 deletions src/net/udpsock.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,18 @@ func (c *UDPConn) ReadFrom(b []byte) (int, Addr, error) {
return n, addr, err
}

// ReadFromUDPAddrPort acts like ReadFrom but returns a netip.AddrPort.
func (c *UDPConn) ReadFromUDPAddrPort(b []byte) (n int, addr netip.AddrPort, err error) {
if !c.ok() {
return 0, netip.AddrPort{}, syscall.EINVAL
}
n, addr, err = c.readFromAddrPort(b)
if err != nil {
err = &OpError{Op: "read", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err}
}
return n, addr, err
}

// ReadMsgUDP reads a message from c, copying the payload into b and
// the associated out-of-band data into oob. It returns the number of
// bytes copied into b, the number of bytes copied into oob, the flags
Expand Down
19 changes: 19 additions & 0 deletions src/net/udpsock_plan9.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,25 @@ func (c *UDPConn) readFrom(b []byte, addr *UDPAddr) (int, *UDPAddr, error) {
return n, addr, nil
}

func (c *UDPConn) readFromAddrPort(b []byte) (int, netip.AddrPort, error) {
// TODO: optimize. The equivalent code on posix is alloc-free.
buf := make([]byte, udpHeaderSize+len(b))
m, err := c.fd.Read(buf)
if err != nil {
return 0, netip.AddrPort{}, err
}
if m < udpHeaderSize {
return 0, netip.AddrPort{}, errors.New("short read reading UDP header")
}
buf = buf[:m]

h, buf := unmarshalUDPHeader(buf)
n := copy(b, buf)
ip, _ := netip.AddrFromSlice(h.raddr)
addr := netip.AddrPortFrom(ip, h.rport)
return n, addr, nil
}

func (c *UDPConn) readMsg(b, oob []byte) (n, oobn, flags int, addr netip.AddrPort, err error) {
return 0, 0, 0, netip.AddrPort{}, syscall.EPLAN9
}
Expand Down
25 changes: 25 additions & 0 deletions src/net/udpsock_posix.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,31 @@ func (c *UDPConn) readFrom(b []byte, addr *UDPAddr) (int, *UDPAddr, error) {
return n, addr, err
}

func (c *UDPConn) readFromAddrPort(b []byte) (n int, addr netip.AddrPort, err error) {
var ip netip.Addr
var port int
switch c.fd.family {
case syscall.AF_INET:
var from syscall.SockaddrInet4
n, err = c.fd.readFromInet4(b, &from)
if err == nil {
ip = netip.AddrFrom4(from.Addr)
port = from.Port
}
case syscall.AF_INET6:
var from syscall.SockaddrInet6
n, err = c.fd.readFromInet6(b, &from)
if err == nil {
ip = netip.AddrFrom16(from.Addr).WithZone(zoneCache.name(int(from.ZoneId)))
port = from.Port
}
}
if err == nil {
addr = netip.AddrPortFrom(ip, uint16(port))
}
return n, addr, err
}

func (c *UDPConn) readMsg(b, oob []byte) (n, oobn, flags int, addr netip.AddrPort, err error) {
var sa syscall.Sockaddr
n, oobn, flags, sa, err = c.fd.readMsg(b, oob, 0)
Expand Down
2 changes: 1 addition & 1 deletion src/net/udpsock_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -533,7 +533,7 @@ func BenchmarkWriteToReadFromUDPAddrPort(b *testing.B) {
if err != nil {
b.Fatal(err)
}
_, _, err = conn.ReadFromUDP(buf) // TODO: create and use ReadFromUDPAddrPort
_, _, err = conn.ReadFromUDPAddrPort(buf)
if err != nil {
b.Fatal(err)
}
Expand Down

0 comments on commit c702f91

Please sign in to comment.