Skip to content

Commit

Permalink
net: reduce allocation size in ReadFromUDP (experiment)
Browse files Browse the repository at this point in the history
Switch to concrete types. Bring your own object to fill in.

Allocate just enough for the IP byte slice.
The allocation is now just 4 bytes, which puts it in the tiny allocator,
which is much faster.

name                  old time/op    new time/op    delta
WriteToReadFromUDP-8    5.59µs ±10%    6.03µs ± 9%   +7.80%  (p=0.004 n=9+10)

name                  old alloc/op   new alloc/op   delta
WriteToReadFromUDP-8     32.0B ± 0%      4.0B ± 0%  -87.50%  (p=0.000 n=10+10)

name                  old allocs/op  new allocs/op  delta
WriteToReadFromUDP-8      1.00 ± 0%      1.00 ± 0%     ~     (all equal)

Change-Id: Ief506f891b401d28715d22dce6ebda037941924e
  • Loading branch information
josharian committed May 26, 2021
1 parent 1074dae commit 4b4fb83
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 8 deletions.
54 changes: 54 additions & 0 deletions src/internal/poll/fd_unix.go
Expand Up @@ -229,6 +229,60 @@ func (fd *FD) ReadFrom(p []byte) (int, syscall.Sockaddr, error) {
}
}

// ReadFrom wraps the recvfrom network call.
func (fd *FD) ReadFromInet4(p []byte, from *syscall.SockaddrInet4) (int, error) {
if err := fd.readLock(); err != nil {
return 0, err
}
defer fd.readUnlock()
if err := fd.pd.prepareRead(fd.isFile); err != nil {
return 0, err
}
for {
n, err := syscall.RecvfromInet4(fd.Sysfd, p, 0, from)
if err != nil {
if err == syscall.EINTR {
continue
}
n = 0
if err == syscall.EAGAIN && fd.pd.pollable() {
if err = fd.pd.waitRead(fd.isFile); err == nil {
continue
}
}
}
err = fd.eofError(n, err)
return n, err
}
}

// ReadFrom wraps the recvfrom network call.
func (fd *FD) ReadFromInet6(p []byte, from *syscall.SockaddrInet6) (int, error) {
if err := fd.readLock(); err != nil {
return 0, err
}
defer fd.readUnlock()
if err := fd.pd.prepareRead(fd.isFile); err != nil {
return 0, err
}
for {
n, err := syscall.RecvfromInet6(fd.Sysfd, p, 0, from)
if err != nil {
if err == syscall.EINTR {
continue
}
n = 0
if err == syscall.EAGAIN && fd.pd.pollable() {
if err = fd.pd.waitRead(fd.isFile); err == nil {
continue
}
}
}
err = fd.eofError(n, err)
return n, err
}
}

// ReadMsg wraps the recvmsg network call.
func (fd *FD) ReadMsg(p []byte, oob []byte) (int, int, int, syscall.Sockaddr, error) {
if err := fd.readLock(); err != nil {
Expand Down
12 changes: 12 additions & 0 deletions src/net/fd_posix.go
Expand Up @@ -63,6 +63,18 @@ func (fd *netFD) readFrom(p []byte) (n int, sa syscall.Sockaddr, err error) {
return n, sa, wrapSyscallError(readFromSyscallName, err)
}

func (fd *netFD) readFromInet4(p []byte, from *syscall.SockaddrInet4) (n int, err error) {
n, err = fd.pfd.ReadFromInet4(p, from)
runtime.KeepAlive(fd)
return n, wrapSyscallError(readFromSyscallName, err)
}

func (fd *netFD) readFromInet6(p []byte, from *syscall.SockaddrInet6) (n int, err error) {
n, err = fd.pfd.ReadFromInet6(p, from)
runtime.KeepAlive(fd)
return n, wrapSyscallError(readFromSyscallName, err)
}

func (fd *netFD) readMsg(p []byte, oob []byte) (n, oobn, flags int, sa syscall.Sockaddr, err error) {
n, oobn, flags, sa, err = fd.pfd.ReadMsg(p, oob)
runtime.KeepAlive(fd)
Expand Down
23 changes: 15 additions & 8 deletions src/net/udpsock_posix.go
Expand Up @@ -42,15 +42,22 @@ func (a *UDPAddr) toLocal(net string) sockaddr {
return &UDPAddr{loopbackIP(net), a.Port, a.Zone}
}

func (c *UDPConn) readFrom(b []byte, addr *UDPAddr) (int, *UDPAddr, error) {
n, sa, err := c.fd.readFrom(b)
switch sa := sa.(type) {
case *syscall.SockaddrInet4:
*addr = UDPAddr{IP: sa.Addr[0:], Port: sa.Port}
case *syscall.SockaddrInet6:
*addr = UDPAddr{IP: sa.Addr[0:], Port: sa.Port, Zone: zoneCache.name(int(sa.ZoneId))}
func (c *UDPConn) readFrom(b []byte, a *UDPAddr) (n int, addr *UDPAddr, err error) {
switch c.fd.family {
case syscall.AF_INET:
var from syscall.SockaddrInet4
n, err = c.fd.readFromInet4(b, &from)
ipbuf := make([]byte, 4)
copy(ipbuf, from.Addr[:])
*a = UDPAddr{IP: ipbuf, Port: from.Port}
case syscall.AF_INET6:
var from syscall.SockaddrInet6
n, err = c.fd.readFromInet6(b, &from)
ipbuf := make([]byte, 16)
copy(ipbuf, from.Addr[:])
*a = UDPAddr{IP: ipbuf, Port: from.Port, Zone: zoneCache.name(int(from.ZoneId))}
}
return n, addr, err
return n, a, err
}

func (c *UDPConn) readMsg(b, oob []byte) (n, oobn, flags int, addr *UDPAddr, err error) {
Expand Down
31 changes: 31 additions & 0 deletions src/syscall/syscall_unix.go
Expand Up @@ -290,6 +290,37 @@ func Recvfrom(fd int, p []byte, flags int) (n int, from Sockaddr, err error) {
return
}

func RecvfromInet4(fd int, p []byte, flags int, from *SockaddrInet4) (n int, err error) {
var rsa RawSockaddrAny
var socklen _Socklen = SizeofSockaddrAny
if n, err = recvfrom(fd, p, flags, &rsa, &socklen); err != nil {
return
}
pp := (*RawSockaddrInet4)(unsafe.Pointer(&rsa))
port := (*[2]byte)(unsafe.Pointer(&pp.Port))
from.Port = int(port[0])<<8 + int(port[1])
for i := 0; i < len(from.Addr); i++ {
from.Addr[i] = pp.Addr[i]
}
return
}

func RecvfromInet6(fd int, p []byte, flags int, from *SockaddrInet6) (n int, err error) {
var rsa RawSockaddrAny
var socklen _Socklen = SizeofSockaddrAny
if n, err = recvfrom(fd, p, flags, &rsa, &socklen); err != nil {
return
}
pp := (*RawSockaddrInet6)(unsafe.Pointer(&rsa))
port := (*[2]byte)(unsafe.Pointer(&pp.Port))
from.Port = int(port[0])<<8 + int(port[1])
from.ZoneId = pp.Scope_id
for i := 0; i < len(pp.Addr); i++ {
from.Addr[i] = pp.Addr[i]
}
return
}

func SendtoInet4(fd int, p []byte, flags int, to SockaddrInet4) (err error) {
ptr, n, err := to.sockaddr()
if err != nil {
Expand Down

0 comments on commit 4b4fb83

Please sign in to comment.