This patch is incomplete, but it is mostly to demonstrate something
interesting I noticed. It looks like 'join' spends a large portion of
it's time in iswblank even when LC_ALL=C.
The easiest way to see this is with widely separated fields, as shown
in the commit message. Note that 'join -t' can likely be optimized
with some inspiration from bytesearch_field_delim_ok in src/cut.c.
-- 8< --
$ export LC_ALL=C
$ seq 10000000 | perl -ple '$_ = $_ . (" " x 100) . $_' \
| LC_ALL=C sort >file1
$ seq 20000000 | perl -ple '$_ = $_ . (" " x 100) . $_' \
| LC_ALL=C sort >file2
$ time ./src/join file1 file2 > /dev/null
real 0m6.216s
user 0m5.458s
sys 0m0.714s
$ time join file1 file2 > /dev/null
real 0m14.260s
user 0m13.537s
sys 0m0.655s
* src/join.c (xfields): Prefer strspn and strcspn in ASCII locales.
---
src/join.c | 32 ++++++++++++++++++++++++++------
1 file changed, 26 insertions(+), 6 deletions(-)
diff --git a/src/join.c b/src/join.c
index 4346758a6..4c3c1829a 100644
--- a/src/join.c
+++ b/src/join.c
@@ -323,12 +323,32 @@ xfields (struct line *line)
return;
if (!tab.len)
- while ((ptr = skip_buf_matching (ptr, lim, newline_or_blank, true)) < lim)
- {
- char *sep = skip_buf_matching (ptr, lim, newline_or_blank, false);
- extract_field (line, ptr, sep - ptr);
- ptr = sep;
- }
+ {
+ if (MB_CUR_MAX <= 1)
+ {
+ do
+ {
+ char *start = ptr + strspn (ptr, "\n\t ");
+ char *end = start + strcspn (start, "\n\t ");
+ idx_t const len = end - start;
+ if (0 < len)
+ extract_field (line, start, len);
+ ptr = end + (ptr < lim);
+ }
+ while (ptr < lim);
+ }
+ else
+ {
+ while ((ptr = skip_buf_matching (ptr, lim, newline_or_blank, true))
+ < lim)
+ {
+ char *sep = skip_buf_matching (ptr, lim, newline_or_blank,
+ false);
+ extract_field (line, ptr, sep - ptr);
+ ptr = sep;
+ }
+ }
+ }
else
{
if (tab.ch != '\n')
--
2.55.0