Avoiding calling memchr2() for fields less than 16 bytes,
results in a 40% performance boost.
$ yes $(yes eeeaae | head -n9 | paste -s -d' ') | head -n1M > asw.in
$ time LC_ALL=C src/cut-before -f10 -w asw.in >/dev/null
real 0m0.254s
$ time LC_ALL=C src/cut-after -f10 -w asw.in >/dev/null
real 0m0.151s
* src/cut.c (find_c_blank): A wrapper around memchr2()
to avoid its significant setup cost.
---
src/cut.c | 19 ++++++++++++++++++-
1 file changed, 18 insertions(+), 1 deletion(-)
diff --git a/src/cut.c b/src/cut.c
index 2e6d8bf82..aa38e72c9 100644
--- a/src/cut.c
+++ b/src/cut.c
@@ -658,6 +658,23 @@ find_field_delim (char *buf, size_t len)
#endif
}
+/* Return a pointer to the next TAB or SPACE in BUF, searching LEN bytes.
+ Return NULL if none is found.
+ It works for all unibyte locales, i.e., assumes they match c_isblank. */
+
+ATTRIBUTE_PURE
+static char *
+find_c_blank (char *buf, size_t len)
+{
+ /* Avoid memchr2 setup for short fields. */
+ idx_t n = MIN (len, 16);
+ for (idx_t i = 0; i < n; i++)
+ if (c_isblank (buf[i]))
+ return buf + i;
+
+ return memchr2 (buf + n, ' ', '\t', len - n);
+}
+
/* Return the number of trailing bytes in BUF that could be the initial
bytes of a delimiter split across buffers. */
@@ -711,7 +728,7 @@ find_field_terminator (char *buf, idx_t len,
idx_t field_len = ctx->line_end ? ctx->line_end - buf : len;
char *field_end = (ctx->blank_delimited
- ? memchr2 (buf, ' ', '\t', field_len)
+ ? find_c_blank (buf, field_len)
: find_field_delim (buf, field_len));
if (field_end)
--
2.55.0