]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Speed up lib/dns/gen.c
authorTony Finch <fanf@isc.org>
Thu, 24 Nov 2022 17:38:16 +0000 (17:38 +0000)
committerTony Finch <dot@dotat.at>
Mon, 28 Nov 2022 09:44:26 +0000 (09:44 +0000)
The `gen` program was causing a lengthy single-threaded pause in
the BIND build. When generating RDATATYPE_FROMTEXT_SW(), `gen` hit
the inner loop of `find_typename()` over 1.2 billion times. This
change avoids long deeply-nested loops, so `gen` now runs in less
than 10ms, about 300x faster.

No changes to the output.

CHANGES
lib/dns/gen.c

diff --git a/CHANGES b/CHANGES
index cf143a47ead8ac4a86bcbd6f777441bc8cb2946c..88d39ab1ce4a923e419f3c28f8b60d91d8ab6b85 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,6 @@
+6028.  [performance]   Build-time code generation of DNS RRtype switches
+                       is now much faster. [GL !7121]
+
 6027.  [bug]           Fix assertion failure in isc_http API used by
                        statschannel if the read callback would be called
                        on HTTP request that has been already closed.
index fcf905e86f4c261cb27811a87660af6c2533068f..5003fc02ff0cb2e1d32720a29c900793ed88799a 100644 (file)
@@ -135,7 +135,7 @@ static const char copyright[] = "/*\n"
 
 static struct cc {
        struct cc *next;
-       int rdclass;
+       uint16_t rdclass;
        char classbuf[TYPECLASSBUF];
 } * classes;
 
@@ -152,11 +152,12 @@ static struct ttnam {
        char typebuf[TYPECLASSBUF];
        char macroname[TYPECLASSBUF];
        char attr[ATTRIBUTESIZE];
-       unsigned int sorted;
+       bool todo;
+       uint8_t hash;
        uint16_t type;
 } typenames[TYPENAMES];
 
-static int maxtype = -1;
+static int ttnam_count;
 
 typedef struct {
        DIR *handle;
@@ -177,6 +178,19 @@ sd(unsigned int, const char *, const char *, char);
 static void
 insert_into_typenames(int, const char *, const char *);
 
+static uint8_t
+HASH(char *string) {
+       size_t n;
+       unsigned char a, b;
+
+       n = strlen(string);
+       INSIST(n > 0);
+       a = tolower((unsigned char)string[0]);
+       b = tolower((unsigned char)string[n - 1]);
+
+       return (((a + n) * b) % 256);
+}
+
 static bool
 start_directory(const char *path, isc_dir_t *dir) {
        dir->handle = opendir(path);
@@ -336,18 +350,6 @@ doswitch(const char *name, const char *function, const char *args,
        }
 }
 
-static struct ttnam *
-find_typename(int type) {
-       int i;
-
-       for (i = 0; i < TYPENAMES; i++) {
-               if (typenames[i].typebuf[0] != 0 && typenames[i].type == type) {
-                       return (&typenames[i]);
-               }
-       }
-       return (NULL);
-}
-
 static void
 insert_into_typenames(int type, const char *typebuf, const char *attr) {
        struct ttnam *ttn = NULL;
@@ -356,22 +358,25 @@ insert_into_typenames(int type, const char *typebuf, const char *attr) {
        char tmp[256];
 
        INSIST(strlen(typebuf) < TYPECLASSBUF);
-       for (i = 0; i < TYPENAMES; i++) {
-               if (typenames[i].typebuf[0] != 0 && typenames[i].type == type &&
-                   strcmp(typebuf, typenames[i].typebuf) != 0)
-               {
+       for (i = 0; i < TYPENAMES && typenames[i].typebuf[0] != 0; i++) {
+               if (typenames[i].type != type) {
+                       continue;
+               }
+               if (strcmp(typebuf, typenames[i].typebuf) != 0) {
                        fprintf(stderr,
-                               "Error:  type %d has two names: %s, %s\n", type,
+                               "Error: type %d has two names: %s, %s\n", type,
                                typenames[i].typebuf, typebuf);
                        exit(1);
                }
-               if (typenames[i].typebuf[0] == 0 && ttn == NULL) {
-                       ttn = &typenames[i];
-               }
+               ttn = &typenames[i];
        }
-       if (ttn == NULL) {
+       /* Subtract one to leave an empty sentinel entry at the end */
+       if (i >= TYPENAMES - 1) {
                fprintf(stderr, "Error: typenames array too small\n");
                exit(1);
+       } else if (ttn == NULL) {
+               ttn = &typenames[i];
+               ttnam_count = i + 1;
        }
 
        /* XXXMUKS: This is redundant due to the INSIST above. */
@@ -419,10 +424,8 @@ insert_into_typenames(int type, const char *typebuf, const char *attr) {
        strncpy(ttn->attr, attr, sizeof(ttn->attr));
        ttn->attr[sizeof(ttn->attr) - 1] = '\0';
 
-       ttn->sorted = 0;
-       if (maxtype < type) {
-               maxtype = type;
-       }
+       ttn->hash = HASH(ttn->typebuf);
+       ttn->todo = true;
 }
 
 static void
@@ -469,16 +472,12 @@ add(unsigned int rdclass, const char *classbuf, int type, const char *typebuf,
        }
 
        while ((tt != NULL) && (tt->type == type) && (tt->rdclass < rdclass)) {
-               if (strcmp(tt->typebuf, typebuf) != 0) {
-                       exit(1);
-               }
+               INSIST(strcmp(tt->typebuf, typebuf) == 0);
                oldtt = tt;
                tt = tt->next;
        }
 
-       if ((tt != NULL) && (tt->type == type) && (tt->rdclass == rdclass)) {
-               exit(1);
-       }
+       INSIST(tt == NULL || tt->type != type || tt->rdclass != rdclass);
 
        newtt->next = tt;
        if (oldtt != NULL) {
@@ -563,20 +562,10 @@ sd(unsigned int rdclass, const char *classbuf, const char *dirbuf,
        end_directory(&dir);
 }
 
-static unsigned int
-HASH(char *string) {
-       size_t n;
-       unsigned char a, b;
-
-       n = strlen(string);
-       if (n == 0) {
-               fprintf(stderr, "n == 0?\n");
-               exit(1);
-       }
-       a = tolower((unsigned char)string[0]);
-       b = tolower((unsigned char)string[n - 1]);
-
-       return (((a + n) * b) % 256);
+static int
+ttnam_cmp(const void *va, const void *vb) {
+       const struct ttnam *ttna = va, *ttnb = vb;
+       return (ttna->type - ttnb->type);
 }
 
 int
@@ -588,7 +577,6 @@ main(int argc, char **argv) {
        struct tt *tt;
        struct cc *cc;
        struct ttnam *ttn, *ttn2;
-       unsigned int hash;
        time_t now;
        char year[11];
        int lasttype;
@@ -597,7 +585,7 @@ main(int argc, char **argv) {
        int type_enum = 0;
        int structs = 0;
        int depend = 0;
-       int c, i, j, n;
+       int c, n;
        char buf1[TYPECLASSBUF];
        char filetype = 'c';
        FILE *fd;
@@ -609,10 +597,6 @@ main(int argc, char **argv) {
        char *endptr;
        isc_dir_t dir;
 
-       for (i = 0; i < TYPENAMES; i++) {
-               memset(&typenames[i], 0, sizeof(typenames[i]));
-       }
-
        srcdir[0] = '\0';
        while ((c = getopt(argc, argv, "cdits:F:P:S:")) != -1) {
                switch (c) {
@@ -676,9 +660,7 @@ main(int argc, char **argv) {
        n = snprintf(buf, sizeof(buf), "%srdata", srcdir);
        INSIST(n > 0 && (unsigned int)n < sizeof(srcdir));
 
-       if (!start_directory(buf, &dir)) {
-               exit(1);
-       }
+       INSIST(start_directory(buf, &dir));
 
        while (next_file(&dir)) {
                if (sscanf(dir.filename, TYPECLASSFMT, classbuf, &rdclass) != 2)
@@ -816,8 +798,6 @@ main(int argc, char **argv) {
                 * attributes.
                 */
 
-#define PRINT_COMMA(x) (x == maxtype ? "" : ",")
-
 #define METANOTQUESTION             \
        "DNS_RDATATYPEATTR_META | " \
        "DNS_RDATATYPEATTR_NOTQUESTION"
@@ -841,6 +821,12 @@ main(int argc, char **argv) {
                insert_into_typenames(254, "maila", METAQUESTIONONLY);
                insert_into_typenames(255, "any", METAQUESTIONONLY);
 
+               /*
+                * For reproducible builds, ensure the directory read
+                * order does not affect the order of switch cases.
+                */
+               qsort(typenames, ttnam_count, sizeof(struct ttnam), ttnam_cmp);
+
                /*
                 * Spit out a quick and dirty hash function.  Here,
                 * we walk through the list of type names, and calculate
@@ -867,37 +853,27 @@ main(int argc, char **argv) {
                printf("#define RDATATYPE_FROMTEXT_SW(_hash,"
                       "_typename,_length,_typep) \\\n");
                printf("\tswitch (_hash) { \\\n");
-               for (i = 0; i <= maxtype; i++) {
-                       ttn = find_typename(i);
-                       if (ttn == NULL) {
-                               continue;
-                       }
-
+               for (ttn = typenames; ttn->typebuf[0] != 0; ttn++) {
                        /*
                         * Skip entries we already processed.
                         */
-                       if (ttn->sorted != 0) {
+                       if (!ttn->todo) {
                                continue;
                        }
 
-                       hash = HASH(ttn->typebuf);
-                       printf("\t\tcase %u: \\\n", hash);
+                       printf("\t\tcase %u: \\\n", ttn->hash);
 
                        /*
                         * Find all other entries that happen to match
                         * this hash.
                         */
-                       for (j = 0; j <= maxtype; j++) {
-                               ttn2 = find_typename(j);
-                               if (ttn2 == NULL) {
-                                       continue;
-                               }
-                               if (hash == HASH(ttn2->typebuf)) {
+                       for (ttn2 = typenames; ttn2->typebuf[0] != 0; ttn2++) {
+                               if (ttn2->todo && ttn2->hash == ttn->hash) {
                                        printf("\t\t\tRDATATYPE_COMPARE"
                                               "(\"%s\", %d, _typename, "
                                               " _length, _typep); \\\n",
                                               ttn2->typebuf, ttn2->type);
-                                       ttn2->sorted = 1;
+                                       ttn2->todo = false;
                                }
                        }
                        printf("\t\t\tbreak; \\\n");
@@ -906,35 +882,27 @@ main(int argc, char **argv) {
 
                printf("#define RDATATYPE_ATTRIBUTE_SW \\\n");
                printf("\tswitch (type) { \\\n");
-               for (i = 0; i <= maxtype; i++) {
-                       ttn = find_typename(i);
-                       if (ttn == NULL) {
-                               continue;
-                       }
-                       printf("\tcase %d: return (%s); \\\n", i,
+               for (ttn = typenames; ttn->typebuf[0] != 0; ttn++) {
+                       printf("\tcase %d: return (%s); \\\n", ttn->type,
                               upper(ttn->attr));
                }
                printf("\t}\n");
 
                printf("#define RDATATYPE_TOTEXT_SW \\\n");
                printf("\tswitch (type) { \\\n");
-               for (i = 0; i <= maxtype; i++) {
-                       ttn = find_typename(i);
-                       if (ttn == NULL) {
-                               continue;
-                       }
+               for (ttn = typenames; ttn->typebuf[0] != 0; ttn++) {
                        /*
                         * Remove KEYDATA (65533) from the type to memonic
                         * translation as it is internal use only.  This
                         * stops the tools from displaying KEYDATA instead
                         * of TYPE65533.
                         */
-                       if (i == 65533U) {
+                       if (ttn->type == 65533U) {
                                continue;
                        }
                        printf("\tcase %d: return "
                               "(str_totext(\"%s\", target)); \\\n",
-                              i, upper(ttn->typebuf));
+                              ttn->type, upper(ttn->typebuf));
                }
                printf("\t}\n");
        } else if (type_enum) {