]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Tests and benchmark for isc_ascii
authorTony Finch <fanf@isc.org>
Sat, 25 Jun 2022 14:29:54 +0000 (15:29 +0100)
committerTony Finch <fanf@isc.org>
Mon, 12 Sep 2022 11:23:39 +0000 (12:23 +0100)
The test is to verify basic functionality. The benchmark compares a
number of alternative tolower() implementations on large and small
strings.

configure.ac
tests/Makefile.am
tests/bench/.gitignore [new file with mode: 0644]
tests/bench/Makefile.am [new file with mode: 0644]
tests/bench/ascii.c [new file with mode: 0644]
tests/isc/Makefile.am
tests/isc/ascii_test.c [new file with mode: 0644]

index 59dfdae3a405c7e00a2e92a7b7d05a6dcca4d5e6..a39afef720ab84c0fd6ebbb2194cc76c2cf7d7be 100644 (file)
@@ -1579,9 +1579,10 @@ AC_CONFIG_FILES([doc/Makefile
                 doc/man/Makefile
                 doc/misc/Makefile])
 
-# Unit Tests
+# Unit tests and benchmarks
 
 AC_CONFIG_FILES([tests/Makefile
+                tests/bench/Makefile
                 tests/isc/Makefile
                 tests/dns/Makefile
                 tests/ns/Makefile
index 8b5dcbe842154ee1e23d5779dbb6ec4b3ba8ac39..3a1a0241fef2d4d13e8f9403840c9a167bfadf44 100644 (file)
@@ -12,5 +12,6 @@ LDADD +=                      \
        $(LIBDNS_LIBS)          \
        $(LIBNS_LIBS)
 
-SUBDIRS = libtest isc dns ns isccfg irs
+SUBDIRS = libtest isc dns ns isccfg irs bench
+
 check_PROGRAMS =
diff --git a/tests/bench/.gitignore b/tests/bench/.gitignore
new file mode 100644 (file)
index 0000000..c9c3825
--- /dev/null
@@ -0,0 +1 @@
+ascii
diff --git a/tests/bench/Makefile.am b/tests/bench/Makefile.am
new file mode 100644 (file)
index 0000000..171db37
--- /dev/null
@@ -0,0 +1,10 @@
+include $(top_srcdir)/Makefile.top
+
+AM_CPPFLAGS +=                 \
+       $(LIBISC_CFLAGS)
+
+LDADD +=                       \
+       $(LIBISC_LIBS)
+
+noinst_PROGRAMS =              \
+       ascii
diff --git a/tests/bench/ascii.c b/tests/bench/ascii.c
new file mode 100644 (file)
index 0000000..dbc62d3
--- /dev/null
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <isc/ascii.h>
+#include <isc/random.h>
+#include <isc/time.h>
+
+#define SIZE (1024 * 1024)
+
+typedef void
+copy_fn(void *a, void *b, unsigned len);
+
+static void
+time_it(copy_fn *copier, void *a, void *b, const char *name) {
+       isc_time_t start;
+       isc_time_now_hires(&start);
+
+       copier(a, b, SIZE);
+
+       isc_time_t finish;
+       isc_time_now_hires(&finish);
+
+       uint64_t microseconds = isc_time_microdiff(&finish, &start);
+       printf("%f for %s\n", (double)microseconds / 1000000.0, name);
+}
+
+static void
+copy_raw(void *a, void *b, unsigned size) {
+       memmove(a, b, size);
+}
+
+static void
+copy_toupper(void *va, void *vb, unsigned size) {
+       uint8_t *a = va, *b = vb;
+       while (size-- > 0) {
+               *a++ = isc_ascii_toupper(*b++);
+       }
+}
+
+static void
+copy_tolower8(void *a, void *b, unsigned size) {
+       isc_ascii_lowercopy(a, b, size);
+}
+
+#define TOLOWER(c) ((c) + ('a' - 'A') * (((c) >= 'A') ^ ((c) > 'Z')))
+
+static void
+copy_tolower1(void *va, void *vb, unsigned size) {
+       for (uint8_t *a = va, *b = vb; size-- > 0; a++, b++) {
+               *a = TOLOWER(*b);
+       }
+}
+
+static bool
+cmp_tolower1(void *va, void *vb, unsigned size) {
+       for (uint8_t *a = va, *b = vb; size-- > 0; a++, b++) {
+               if (TOLOWER(*a) != TOLOWER(*b)) {
+                       return false;
+               }
+       }
+       return true;
+}
+
+static bool oldskool_result;
+
+static void
+cmp_oldskool(void *va, void *vb, unsigned size) {
+       uint8_t *a = va, *b = vb, c;
+
+       while (size > 3) {
+               c = isc_ascii_tolower(a[0]);
+               if (c != isc_ascii_tolower(b[0])) {
+                       goto diff;
+               }
+               c = isc_ascii_tolower(a[1]);
+               if (c != isc_ascii_tolower(b[1])) {
+                       goto diff;
+               }
+               c = isc_ascii_tolower(a[2]);
+               if (c != isc_ascii_tolower(b[2])) {
+                       goto diff;
+               }
+               c = isc_ascii_tolower(a[3]);
+               if (c != isc_ascii_tolower(b[3])) {
+                       goto diff;
+               }
+               size -= 4;
+               a += 4;
+               b += 4;
+       }
+       while (size-- > 0) {
+               c = isc_ascii_tolower(*a++);
+               if (c != isc_ascii_tolower(*b++)) {
+                       goto diff;
+               }
+       }
+       oldskool_result = true;
+       return;
+diff:
+       oldskool_result = false;
+       return;
+}
+
+static bool tolower1_result;
+
+static void
+vcmp_tolower1(void *a, void *b, unsigned size) {
+       tolower1_result = cmp_tolower1(a, b, size);
+}
+
+static bool swar_result;
+
+static void
+cmp_swar(void *a, void *b, unsigned size) {
+       swar_result = isc_ascii_lowerequal(a, b, size);
+}
+
+static bool chunk_result;
+static unsigned chunk_size;
+
+static void
+cmp_chunks1(void *va, void *vb, unsigned size) {
+       uint8_t *a = va, *b = vb;
+
+       chunk_result = false;
+       while (size >= chunk_size) {
+               if (!cmp_tolower1(a, b, chunk_size)) {
+                       return;
+               }
+               size -= chunk_size;
+               a += chunk_size;
+               b += chunk_size;
+       }
+       chunk_result = cmp_tolower1(a, b, size);
+}
+
+static void
+cmp_chunks8(void *va, void *vb, unsigned size) {
+       uint8_t *a = va, *b = vb;
+
+       while (size >= chunk_size) {
+               if (!isc_ascii_lowerequal(a, b, chunk_size)) {
+                       goto diff;
+               }
+               size -= chunk_size;
+               a += chunk_size;
+               b += chunk_size;
+       }
+       chunk_result = isc_ascii_lowerequal(a, b, size);
+       return;
+diff:
+       chunk_result = false;
+       return;
+}
+
+static void
+cmp_oldchunks(void *va, void *vb, unsigned size) {
+       uint8_t *a = va, *b = vb;
+
+       while (size >= chunk_size) {
+               cmp_oldskool(a, b, chunk_size);
+               if (!oldskool_result) {
+                       return;
+               }
+               size -= chunk_size;
+               a += chunk_size;
+               b += chunk_size;
+       }
+       cmp_oldskool(a, b, size);
+}
+
+int
+main(void) {
+       static uint8_t bytes[SIZE];
+
+       isc_random_buf(bytes, SIZE);
+
+       static uint8_t raw_dest[SIZE];
+       time_it(copy_raw, raw_dest, bytes, "memmove");
+
+       static uint8_t toupper_dest[SIZE];
+       time_it(copy_toupper, toupper_dest, bytes, "toupper");
+
+       static uint8_t tolower1_dest[SIZE];
+       time_it(copy_tolower1, tolower1_dest, bytes, "tolower1");
+
+       static uint8_t tolower8_dest[SIZE];
+       time_it(copy_tolower8, tolower8_dest, bytes, "tolower8");
+
+       time_it(cmp_oldskool, toupper_dest, tolower1_dest, "oldskool");
+       printf("-> %s\n", oldskool_result ? "same" : "WAT");
+
+       time_it(vcmp_tolower1, tolower1_dest, tolower8_dest, "tolower1");
+       printf("-> %s\n", tolower1_result ? "same" : "WAT");
+
+       time_it(cmp_swar, toupper_dest, tolower8_dest, "swar");
+       printf("-> %s\n", swar_result ? "same" : "WAT");
+
+       for (chunk_size = 3; chunk_size <= 15; chunk_size += 2) {
+               time_it(cmp_chunks1, toupper_dest, raw_dest, "chunks1");
+               printf("%u -> %s\n", chunk_size, chunk_result ? "same" : "WAT");
+               time_it(cmp_chunks8, toupper_dest, raw_dest, "chunks8");
+               printf("%u -> %s\n", chunk_size, chunk_result ? "same" : "WAT");
+               time_it(cmp_oldchunks, toupper_dest, raw_dest, "oldchunks");
+               printf("%u -> %s\n", chunk_size,
+                      oldskool_result ? "same" : "WAT");
+       }
+}
index 59b1acd79e33bc75f745a6ba60b9af75dcd72cde..a9fd5a61371c8dc7b8eaa3232d0368823158dcee 100644 (file)
@@ -10,6 +10,7 @@ LDADD +=                      \
        $(LIBUV_LIBS)
 
 check_PROGRAMS =       \
+       ascii_test      \
        aes_test        \
        async_test      \
        buffer_test     \
diff --git a/tests/isc/ascii_test.c b/tests/isc/ascii_test.c
new file mode 100644 (file)
index 0000000..43be4ce
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <ctype.h>
+#include <sched.h> /* IWYU pragma: keep */
+#include <setjmp.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#define UNIT_TESTING
+#include <cmocka.h>
+
+#include <isc/ascii.h>
+
+#include <tests/isc.h>
+
+const char *same[][2] = {
+       {
+               "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz",
+               "aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz",
+       },
+       {
+               "aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz",
+               "AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ",
+       },
+       {
+               "AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ",
+               "aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz",
+       },
+       {
+               "aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ",
+               "aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz",
+       },
+       {
+               "aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVxXyYzZ",
+               "aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvxxyyzz",
+       },
+       {
+               "WwW.ExAmPlE.OrG",
+               "wWw.eXaMpLe.oRg",
+       },
+       {
+               "_SIP.tcp.example.org",
+               "_sip.TCP.example.org",
+       },
+       {
+               "bind-USERS.lists.example.org",
+               "bind-users.lists.example.org",
+       },
+       {
+               "a0123456789.example.org",
+               "A0123456789.example.org",
+       },
+       {
+               "\\000.example.org",
+               "\\000.example.org",
+       },
+       {
+               "wWw.\\000.isc.org",
+               "www.\\000.isc.org",
+       },
+       {
+               "\255.example.org",
+               "\255.example.ORG",
+       }
+};
+
+struct {
+       const char *a, *b;
+       int cmp;
+} diff[] = {
+       { "foo", "bar", +1 },
+       { "bar", "foo", -1 },
+       { "foosuffix", "barsuffix", +1 },
+       { "barsuffix", "foosuffix", -1 },
+       { "prefixfoo", "prefixbar", +1 },
+       { "prefixbar", "prefixfoo", -1 },
+};
+
+ISC_RUN_TEST_IMPL(upperlower) {
+       UNUSED(state);
+
+       for (size_t n = 0; n < ARRAY_SIZE(same); n++) {
+               const char *a = same[n][0];
+               const char *b = same[n][1];
+               for (size_t i = 0; a[i] != '\0' && b[i] != '\0'; i++) {
+                       assert_true(isc_ascii_toupper(a[i]) == (uint8_t)a[i] ||
+                                   isc_ascii_tolower(a[i]) == (uint8_t)a[i]);
+                       assert_true(isc_ascii_toupper(b[i]) == (uint8_t)b[i] ||
+                                   isc_ascii_tolower(b[i]) == (uint8_t)b[i]);
+                       assert_true(isc_ascii_toupper(a[i]) ==
+                                   isc_ascii_toupper(b[i]));
+                       assert_true(isc_ascii_tolower(a[i]) ==
+                                   isc_ascii_tolower(b[i]));
+               }
+       }
+}
+
+ISC_RUN_TEST_IMPL(lowerequal) {
+       for (size_t n = 0; n < ARRAY_SIZE(same); n++) {
+               const uint8_t *a = (void *)same[n][0];
+               const uint8_t *b = (void *)same[n][1];
+               unsigned len = (unsigned)strlen(same[n][0]);
+               assert_true(isc_ascii_lowerequal(a, b, len));
+       }
+       for (size_t n = 0; n < ARRAY_SIZE(diff); n++) {
+               const uint8_t *a = (void *)diff[n].a;
+               const uint8_t *b = (void *)diff[n].b;
+               unsigned len = (unsigned)strlen(diff[n].a);
+               assert_true(!isc_ascii_lowerequal(a, b, len));
+       }
+}
+
+ISC_RUN_TEST_IMPL(lowercmp) {
+       for (size_t n = 0; n < ARRAY_SIZE(same); n++) {
+               const uint8_t *a = (void *)same[n][0];
+               const uint8_t *b = (void *)same[n][1];
+               unsigned len = (unsigned)strlen(same[n][0]);
+               assert_true(isc_ascii_lowercmp(a, b, len) == 0);
+       }
+       for (size_t n = 0; n < ARRAY_SIZE(diff); n++) {
+               const uint8_t *a = (void *)diff[n].a;
+               const uint8_t *b = (void *)diff[n].b;
+               unsigned len = (unsigned)strlen(diff[n].a);
+               assert_true(isc_ascii_lowercmp(a, b, len) == diff[n].cmp);
+       }
+}
+
+ISC_RUN_TEST_IMPL(exhaustive) {
+       for (uint64_t ab = 0; ab < (1 << 16); ab++) {
+               uint8_t a = ab >> 8;
+               uint8_t b = ab & 0xFF;
+               uint64_t abc = tolower(a) << 8 | tolower(b);
+               uint64_t abi = isc_ascii_tolower(a) << 8 | isc_ascii_tolower(b);
+               uint64_t ab1 = isc__ascii_tolower1(a) << 8 |
+                              isc__ascii_tolower1(b);
+               uint64_t ab8 = isc__ascii_tolower8(ab);
+               /* each byte individually matches ctype.h */
+               assert_int_equal(tolower(a), isc_ascii_tolower(a));
+               assert_int_equal(tolower(a), isc__ascii_tolower1(a));
+               assert_int_equal(tolower(a), isc__ascii_tolower8(a));
+               assert_int_equal(tolower(b), isc_ascii_tolower(b));
+               assert_int_equal(tolower(b), isc__ascii_tolower1(b));
+               assert_int_equal(tolower(b), isc__ascii_tolower8(b));
+               /* two lanes of SWAR match other implementations */
+               assert_int_equal(ab8, abc);
+               assert_int_equal(ab8, abi);
+               assert_int_equal(ab8, ab1);
+               /* check lack of overflow */
+               assert_int_equal(ab8 >> 16, 0);
+               /* all lanes of SWAR work */
+               assert_int_equal(isc__ascii_tolower8(ab << 8), abc << 8);
+               assert_int_equal(isc__ascii_tolower8(ab << 16), abc << 16);
+               assert_int_equal(isc__ascii_tolower8(ab << 24), abc << 24);
+               assert_int_equal(isc__ascii_tolower8(ab << 32), abc << 32);
+               assert_int_equal(isc__ascii_tolower8(ab << 40), abc << 40);
+               assert_int_equal(isc__ascii_tolower8(ab << 48), abc << 48);
+       }
+}
+
+ISC_TEST_LIST_START
+ISC_TEST_ENTRY(upperlower)
+ISC_TEST_ENTRY(lowerequal)
+ISC_TEST_ENTRY(lowercmp)
+ISC_TEST_ENTRY(exhaustive)
+ISC_TEST_LIST_END
+
+ISC_TEST_MAIN