conn: remove the final alloc per packet receive
This does bind_std only; other platforms remain. The remaining alloc per iteration in the Throughput benchmark comes from the tuntest package, and should not appear in regular use. name old time/op new time/op delta Latency-10 25.2µs ± 1% 25.0µs ± 0% -0.58% (p=0.006 n=10+10) Throughput-10 2.44µs ± 3% 2.41µs ± 2% ~ (p=0.140 n=10+8) name old alloc/op new alloc/op delta Latency-10 854B ± 5% 741B ± 3% -13.22% (p=0.000 n=10+10) Throughput-10 265B ±34% 267B ±39% ~ (p=0.670 n=10+10) name old allocs/op new allocs/op delta Latency-10 16.0 ± 0% 14.0 ± 0% -12.50% (p=0.000 n=10+10) Throughput-10 2.00 ± 0% 1.00 ± 0% -50.00% (p=0.000 n=10+10) name old packet-loss new packet-loss delta Throughput-10 0.01 ±82% 0.01 ±282% ~ (p=0.321 n=9+8) Signed-off-by: Josh Bleecher Snyder <josh@tailscale.com> Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
This commit is contained in:
parent
193cf8d6a5
commit
ef5c587f78
@ -31,34 +31,34 @@ type StdNetEndpoint netip.AddrPort
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
_ Bind = (*StdNetBind)(nil)
|
_ Bind = (*StdNetBind)(nil)
|
||||||
_ Endpoint = (*StdNetEndpoint)(nil)
|
_ Endpoint = StdNetEndpoint{}
|
||||||
)
|
)
|
||||||
|
|
||||||
func (*StdNetBind) ParseEndpoint(s string) (Endpoint, error) {
|
func (*StdNetBind) ParseEndpoint(s string) (Endpoint, error) {
|
||||||
e, err := netip.ParseAddrPort(s)
|
e, err := netip.ParseAddrPort(s)
|
||||||
return (*StdNetEndpoint)(&e), err
|
return asEndpoint(e), err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*StdNetEndpoint) ClearSrc() {}
|
func (StdNetEndpoint) ClearSrc() {}
|
||||||
|
|
||||||
func (e *StdNetEndpoint) DstIP() netip.Addr {
|
func (e StdNetEndpoint) DstIP() netip.Addr {
|
||||||
return (*netip.AddrPort)(e).Addr()
|
return (netip.AddrPort)(e).Addr()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *StdNetEndpoint) SrcIP() netip.Addr {
|
func (e StdNetEndpoint) SrcIP() netip.Addr {
|
||||||
return netip.Addr{} // not supported
|
return netip.Addr{} // not supported
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *StdNetEndpoint) DstToBytes() []byte {
|
func (e StdNetEndpoint) DstToBytes() []byte {
|
||||||
b, _ := (*netip.AddrPort)(e).MarshalBinary()
|
b, _ := (netip.AddrPort)(e).MarshalBinary()
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *StdNetEndpoint) DstToString() string {
|
func (e StdNetEndpoint) DstToString() string {
|
||||||
return (*netip.AddrPort)(e).String()
|
return (netip.AddrPort)(e).String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *StdNetEndpoint) SrcToString() string {
|
func (e StdNetEndpoint) SrcToString() string {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -152,24 +152,24 @@ func (bind *StdNetBind) Close() error {
|
|||||||
func (*StdNetBind) makeReceiveIPv4(conn *net.UDPConn) ReceiveFunc {
|
func (*StdNetBind) makeReceiveIPv4(conn *net.UDPConn) ReceiveFunc {
|
||||||
return func(buff []byte) (int, Endpoint, error) {
|
return func(buff []byte) (int, Endpoint, error) {
|
||||||
n, endpoint, err := conn.ReadFromUDPAddrPort(buff)
|
n, endpoint, err := conn.ReadFromUDPAddrPort(buff)
|
||||||
return n, (*StdNetEndpoint)(&endpoint), err
|
return n, asEndpoint(endpoint), err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*StdNetBind) makeReceiveIPv6(conn *net.UDPConn) ReceiveFunc {
|
func (*StdNetBind) makeReceiveIPv6(conn *net.UDPConn) ReceiveFunc {
|
||||||
return func(buff []byte) (int, Endpoint, error) {
|
return func(buff []byte) (int, Endpoint, error) {
|
||||||
n, endpoint, err := conn.ReadFromUDPAddrPort(buff)
|
n, endpoint, err := conn.ReadFromUDPAddrPort(buff)
|
||||||
return n, (*StdNetEndpoint)(&endpoint), err
|
return n, asEndpoint(endpoint), err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bind *StdNetBind) Send(buff []byte, endpoint Endpoint) error {
|
func (bind *StdNetBind) Send(buff []byte, endpoint Endpoint) error {
|
||||||
var err error
|
var err error
|
||||||
nend, ok := endpoint.(*StdNetEndpoint)
|
nend, ok := endpoint.(StdNetEndpoint)
|
||||||
if !ok {
|
if !ok {
|
||||||
return ErrWrongEndpointType
|
return ErrWrongEndpointType
|
||||||
}
|
}
|
||||||
addrPort := (*netip.AddrPort)(nend)
|
addrPort := netip.AddrPort(nend)
|
||||||
|
|
||||||
bind.mu.Lock()
|
bind.mu.Lock()
|
||||||
blackhole := bind.blackhole4
|
blackhole := bind.blackhole4
|
||||||
@ -186,6 +186,27 @@ func (bind *StdNetBind) Send(buff []byte, endpoint Endpoint) error {
|
|||||||
if conn == nil {
|
if conn == nil {
|
||||||
return syscall.EAFNOSUPPORT
|
return syscall.EAFNOSUPPORT
|
||||||
}
|
}
|
||||||
_, err = conn.WriteToUDPAddrPort(buff, *addrPort)
|
_, err = conn.WriteToUDPAddrPort(buff, addrPort)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// endpointPool contains a re-usable set of mapping from netip.AddrPort to Endpoint.
|
||||||
|
// This exists to reduce allocations: Putting a netip.AddrPort in an Endpoint allocates,
|
||||||
|
// but Endpoints are immutable, so we can re-use them.
|
||||||
|
var endpointPool = sync.Pool{
|
||||||
|
New: func() any {
|
||||||
|
return make(map[netip.AddrPort]Endpoint)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// asEndpoint returns an Endpoint containing ap.
|
||||||
|
func asEndpoint(ap netip.AddrPort) Endpoint {
|
||||||
|
m := endpointPool.Get().(map[netip.AddrPort]Endpoint)
|
||||||
|
defer endpointPool.Put(m)
|
||||||
|
e, ok := m[ap]
|
||||||
|
if !ok {
|
||||||
|
e = Endpoint(StdNetEndpoint(ap))
|
||||||
|
m[ap] = e
|
||||||
|
}
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user