From 0eb6cc932162dbbd53882c16d31a7784b5df8912 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Mon, 13 Sep 2021 08:48:38 -0700 Subject: [PATCH] portlist: cache field index position between runs, cut two more allocs (Linux) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- portlist/portlist_linux.go | 37 +++++++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/portlist/portlist_linux.go b/portlist/portlist_linux.go index cb1a9480a..ee413ca40 100644 --- a/portlist/portlist_linux.go +++ b/portlist/portlist_linux.go @@ -81,7 +81,7 @@ func parsePorts(r *bufio.Reader, fileBase string) ([]Port, error) { var ret []Port // skip header row - _, err := r.ReadString('\n') + _, err := r.ReadSlice('\n') if err != nil { return nil, err } @@ -93,7 +93,20 @@ func parsePorts(r *bufio.Reader, fileBase string) ([]Port, error) { 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 { line, err := r.ReadSlice('\n') if err == io.EOF { @@ -102,9 +115,25 @@ func parsePorts(r *bufio.Reader, fileBase string) ([]Port, error) { if err != nil { return nil, err } + rows++ + if rows >= maxRows { + break + } + if len(line) == 0 { + continue + } - if i := fieldIndex(line, 2); i == -1 || - !mem.HasPrefix(mem.B(line).SliceFrom(i), wantRemote) { + // On the first row of output, find the index of the 3rd field (index 2), + // 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. continue }