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%).
int co_getline(const struct channel *chn, char *str, int len)
{
int ret, max;
- char *p;
+ size_t ofs;
ret = 0;
max = 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)) &&