From 11051ed9c7fc525fa635983846c765c6302f2150 Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Mon, 30 Sep 2024 11:19:20 +0200 Subject: [PATCH] OPTIM: channel: speed up co_getline()'s search of the end of line 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 | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/src/channel.c b/src/channel.c index 47d5dde6e..d120db79e 100644 --- a/src/channel.c +++ b/src/channel.c @@ -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; } - while (max) { - *str++ = *p; - ret++; - max--; - if (*p == '\n') + ofs = 0; + + while (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; + + 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)) &&