Skip to content

Commit ef5c587

Browse files
josharianzx2c4
authored andcommitted
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 <[email protected]> Signed-off-by: Jason A. Donenfeld <[email protected]>
1 parent 193cf8d commit ef5c587

File tree

1 file changed

+37
-16
lines changed

1 file changed

+37
-16
lines changed

conn/bind_std.go

Lines changed: 37 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -31,34 +31,34 @@ type StdNetEndpoint netip.AddrPort
3131

3232
var (
3333
_ Bind = (*StdNetBind)(nil)
34-
_ Endpoint = (*StdNetEndpoint)(nil)
34+
_ Endpoint = StdNetEndpoint{}
3535
)
3636

3737
func (*StdNetBind) ParseEndpoint(s string) (Endpoint, error) {
3838
e, err := netip.ParseAddrPort(s)
39-
return (*StdNetEndpoint)(&e), err
39+
return asEndpoint(e), err
4040
}
4141

42-
func (*StdNetEndpoint) ClearSrc() {}
42+
func (StdNetEndpoint) ClearSrc() {}
4343

44-
func (e *StdNetEndpoint) DstIP() netip.Addr {
45-
return (*netip.AddrPort)(e).Addr()
44+
func (e StdNetEndpoint) DstIP() netip.Addr {
45+
return (netip.AddrPort)(e).Addr()
4646
}
4747

48-
func (e *StdNetEndpoint) SrcIP() netip.Addr {
48+
func (e StdNetEndpoint) SrcIP() netip.Addr {
4949
return netip.Addr{} // not supported
5050
}
5151

52-
func (e *StdNetEndpoint) DstToBytes() []byte {
53-
b, _ := (*netip.AddrPort)(e).MarshalBinary()
52+
func (e StdNetEndpoint) DstToBytes() []byte {
53+
b, _ := (netip.AddrPort)(e).MarshalBinary()
5454
return b
5555
}
5656

57-
func (e *StdNetEndpoint) DstToString() string {
58-
return (*netip.AddrPort)(e).String()
57+
func (e StdNetEndpoint) DstToString() string {
58+
return (netip.AddrPort)(e).String()
5959
}
6060

61-
func (e *StdNetEndpoint) SrcToString() string {
61+
func (e StdNetEndpoint) SrcToString() string {
6262
return ""
6363
}
6464

@@ -152,24 +152,24 @@ func (bind *StdNetBind) Close() error {
152152
func (*StdNetBind) makeReceiveIPv4(conn *net.UDPConn) ReceiveFunc {
153153
return func(buff []byte) (int, Endpoint, error) {
154154
n, endpoint, err := conn.ReadFromUDPAddrPort(buff)
155-
return n, (*StdNetEndpoint)(&endpoint), err
155+
return n, asEndpoint(endpoint), err
156156
}
157157
}
158158

159159
func (*StdNetBind) makeReceiveIPv6(conn *net.UDPConn) ReceiveFunc {
160160
return func(buff []byte) (int, Endpoint, error) {
161161
n, endpoint, err := conn.ReadFromUDPAddrPort(buff)
162-
return n, (*StdNetEndpoint)(&endpoint), err
162+
return n, asEndpoint(endpoint), err
163163
}
164164
}
165165

166166
func (bind *StdNetBind) Send(buff []byte, endpoint Endpoint) error {
167167
var err error
168-
nend, ok := endpoint.(*StdNetEndpoint)
168+
nend, ok := endpoint.(StdNetEndpoint)
169169
if !ok {
170170
return ErrWrongEndpointType
171171
}
172-
addrPort := (*netip.AddrPort)(nend)
172+
addrPort := netip.AddrPort(nend)
173173

174174
bind.mu.Lock()
175175
blackhole := bind.blackhole4
@@ -186,6 +186,27 @@ func (bind *StdNetBind) Send(buff []byte, endpoint Endpoint) error {
186186
if conn == nil {
187187
return syscall.EAFNOSUPPORT
188188
}
189-
_, err = conn.WriteToUDPAddrPort(buff, *addrPort)
189+
_, err = conn.WriteToUDPAddrPort(buff, addrPort)
190190
return err
191191
}
192+
193+
// endpointPool contains a re-usable set of mapping from netip.AddrPort to Endpoint.
194+
// This exists to reduce allocations: Putting a netip.AddrPort in an Endpoint allocates,
195+
// but Endpoints are immutable, so we can re-use them.
196+
var endpointPool = sync.Pool{
197+
New: func() any {
198+
return make(map[netip.AddrPort]Endpoint)
199+
},
200+
}
201+
202+
// asEndpoint returns an Endpoint containing ap.
203+
func asEndpoint(ap netip.AddrPort) Endpoint {
204+
m := endpointPool.Get().(map[netip.AddrPort]Endpoint)
205+
defer endpointPool.Put(m)
206+
e, ok := m[ap]
207+
if !ok {
208+
e = Endpoint(StdNetEndpoint(ap))
209+
m[ap] = e
210+
}
211+
return e
212+
}

0 commit comments

Comments
 (0)