OPTIM: halog: skip fields 64 bits at a time when supported

Some architectures like x86_64 and aarch64 support efficient unaligned
64-bit reads. On such architectures, we already know that each string
passed to field_start() has some margin at the end because it's parsed
using fgets2() which looks for the trailing LF using the same method.
Thus let's skip spaces by packs of 8. This increases the parsing speed
by 35%.
This commit is contained in:
Willy Tarreau 2021-11-08 10:02:52 +01:00
parent fc76bbc0f5
commit e9f4d67b15
1 changed files with 32 additions and 0 deletions

View File

@ -20,6 +20,8 @@
#include <ctype.h> #include <ctype.h>
#include <time.h> #include <time.h>
#include <haproxy/compiler.h>
#include <import/eb32tree.h> #include <import/eb32tree.h>
#include <import/eb64tree.h> #include <import/eb64tree.h>
#include <import/ebistree.h> #include <import/ebistree.h>
@ -253,6 +255,16 @@ const char *field_stop(const char *p)
} }
#endif #endif
/* return non-zero if the argument contains at least one zero byte. See principle above. */
static inline __attribute__((unused)) unsigned long long has_zero64(unsigned long long x)
{
unsigned long long y;
y = x - 0x0101010101010101ULL; /* generate a carry */
y &= ~x; /* clear the bits that were already set */
return y & 0x8080808080808080ULL;
}
/* return field <field> (starting from 1) in string <p>. Only consider /* return field <field> (starting from 1) in string <p>. Only consider
* contiguous spaces (or tabs) as one delimiter. May return pointer to * contiguous spaces (or tabs) as one delimiter. May return pointer to
* last char if field is not found. Equivalent to awk '{print $field}'. * last char if field is not found. Equivalent to awk '{print $field}'.
@ -280,6 +292,26 @@ const char *field_start(const char *p, int field)
/* skip this field */ /* skip this field */
while (1) { while (1) {
#if defined(HA_UNALIGNED_LE64)
unsigned long long l = *(unsigned long long *)p;
if (!has_zero64(l)) {
l ^= 0x2020202020202020;
l = has_zero64(l);
if (!l) {
p += 8;
continue;
}
/* there is at least one space, find it and
* skip it now. The lowest byte in <l> with
* a 0x80 is the right one, but checking for
* it remains slower than testing each byte,
* probably due to the numerous short fields.
*/
while (*(p++) != ' ')
;
break;
}
#endif
c = *(p++); c = *(p++);
if (c == '\0') if (c == '\0')
return p - 1; return p - 1;