portlist: cache field index position between runs, cut two more allocs (Linux)

name          old time/op    new time/op    delta
ParsePorts-6    6.41ms ± 7%    3.15ms ± 2%  -50.84%  (p=0.000 n=9+9)

name          old alloc/op   new alloc/op   delta
ParsePorts-6      408B ± 0%      216B ± 0%  -47.06%  (p=0.002 n=8+10)

name          old allocs/op  new allocs/op  delta
ParsePorts-6      7.00 ± 0%      4.00 ± 0%  -42.86%  (p=0.000 n=10+10)

Updates tailscale/corp#2566

Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
Brad Fitzpatrick 2021-09-13 08:48:38 -07:00 committed by Brad Fitzpatrick
parent 61f201f33d
commit 0eb6cc9321

View File

@ -81,7 +81,7 @@ func parsePorts(r *bufio.Reader, fileBase string) ([]Port, error) {
var ret []Port var ret []Port
// skip header row // skip header row
_, err := r.ReadString('\n') _, err := r.ReadSlice('\n')
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -93,7 +93,20 @@ func parsePorts(r *bufio.Reader, fileBase string) ([]Port, error) {
wantRemote = mem.S(v6Any) wantRemote = mem.S(v6Any)
} }
var inoBuf []byte // remoteIndex is the index within a line to the remote address field.
// -1 means not yet found.
remoteIndex := -1
// Add an upper bound on how many rows we'll attempt to read just
// to make sure this doesn't consume too much of their CPU.
// TODO(bradfitz,crawshaw): adaptively adjust polling interval as function
// of open sockets.
const maxRows = 1e6
rows := 0
// Scratch buffer for making inode strings.
inoBuf := make([]byte, 0, 50)
for err == nil { for err == nil {
line, err := r.ReadSlice('\n') line, err := r.ReadSlice('\n')
if err == io.EOF { if err == io.EOF {
@ -102,9 +115,25 @@ func parsePorts(r *bufio.Reader, fileBase string) ([]Port, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
rows++
if rows >= maxRows {
break
}
if len(line) == 0 {
continue
}
if i := fieldIndex(line, 2); i == -1 || // On the first row of output, find the index of the 3rd field (index 2),
!mem.HasPrefix(mem.B(line).SliceFrom(i), wantRemote) { // the remote address. All the rows are aligned, at least until 4 billion open
// TCP connections, per the Linux get_tcp4_sock's "%4d: " on an int i.
if remoteIndex == -1 {
remoteIndex = fieldIndex(line, 2)
if remoteIndex == -1 {
break
}
}
if len(line) < remoteIndex || !mem.HasPrefix(mem.B(line).SliceFrom(remoteIndex), wantRemote) {
// Fast path for not being a listener port. // Fast path for not being a listener port.
continue continue
} }