]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
OPTIM: halog: skip fields 64 bits at a time when supported
authorWilly Tarreau <w@1wt.eu>
Mon, 8 Nov 2021 09:02:52 +0000 (10:02 +0100)
committerWilly Tarreau <w@1wt.eu>
Mon, 8 Nov 2021 11:08:26 +0000 (12:08 +0100)
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%.

admin/halog/halog.c

index 5c0218b817f0a2316a5c9ce3b510a86d134967d8..8012c48b5b08982ae2fa6e3f754c54e8c83dd7f4 100644 (file)
@@ -20,6 +20,8 @@
 #include <ctype.h>
 #include <time.h>
 
+#include <haproxy/compiler.h>
+
 #include <import/eb32tree.h>
 #include <import/eb64tree.h>
 #include <import/ebistree.h>
@@ -253,6 +255,16 @@ const char *field_stop(const char *p)
 }
 #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
  * contiguous spaces (or tabs) as one delimiter. May return pointer to
  * 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 */
                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++);
                        if (c == '\0')
                                return p - 1;