]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
OPTIM: channel: speed up co_getline()'s search of the end of line
authorWilly Tarreau <w@1wt.eu>
Mon, 30 Sep 2024 09:19:20 +0000 (11:19 +0200)
committerWilly Tarreau <w@1wt.eu>
Mon, 30 Sep 2024 09:36:39 +0000 (11:36 +0200)
Previously, co_getline() was essentially used for occasional parsing
in peers's banner or Lua, so it could afford to read one character at
a time. However now it's also used on the TCP log path, where it can
consume up to 40% CPU as mentioned in GH issue #2731. Let's speed it
up by using memchr() to look for the LF, and copying the data at once
using memcpy().

Previously it would take 2.44s to consume 1 GB of log on a single
thread of a Core i7-8650U, now it takes 1.56s (-36%).

src/channel.c

index 47d5dde6e19527d772fa7943d7322a21452eebb7..d120db79eafa50d95f430315ae24a3f565737607 100644 (file)
@@ -324,7 +324,7 @@ int co_getword(const struct channel *chn, char *str, int len, char sep)
 int co_getline(const struct channel *chn, char *str, int len)
 {
        int ret, max;
-       char *p;
+       size_t ofs;
 
        ret = 0;
        max = len;
@@ -336,20 +336,30 @@ int co_getline(const struct channel *chn, char *str, int len)
                goto out;
        }
 
-       p = co_head(chn);
-
        if (max > co_data(chn)) {
                max = co_data(chn);
                str[max-1] = 0;
        }
+
+       ofs = 0;
+
        while (max) {
-               *str++ = *p;
-               ret++;
-               max--;
+               size_t contig = b_contig_data(&chn->buf, ofs);
+               size_t len = MIN(max, contig);
+               const char *beg = b_peek(&chn->buf, ofs);
+               const char *lf = memchr(beg, '\n', len);
+
+               if (lf) /* take the LF with it before stopping */
+                       len = lf + 1 - beg;
 
-               if (*p == '\n')
+               memcpy(str, beg, len);
+               ret += len;
+               str += len;
+               ofs += len;
+               max -= len;
+
+               if (lf)
                        break;
-               p = b_next(&chn->buf, p);
        }
        if (ret > 0 && ret < len &&
            (ret < co_data(chn) || channel_may_recv(chn)) &&