]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Improve performance of RBT (#41165)
authorMukund Sivaraman <muks@isc.org>
Wed, 9 Dec 2015 13:37:20 +0000 (19:07 +0530)
committerMukund Sivaraman <muks@isc.org>
Wed, 9 Dec 2015 13:40:55 +0000 (19:10 +0530)
17 files changed:
CHANGES
bin/tests/system/dyndb/driver/db.c
lib/dns/acache.c
lib/dns/cache.c
lib/dns/db.c
lib/dns/include/dns/db.h
lib/dns/include/dns/rbt.h
lib/dns/name.c
lib/dns/rbt.c
lib/dns/rbtdb.c
lib/dns/tests/dnstest.c
lib/dns/tests/dnstest.h
lib/dns/tests/rbt_test.c
lib/isc/hash.c
lib/isc/include/isc/hash.h
lib/isc/sockaddr.c
lib/isc/win32/libisc.def.in

diff --git a/CHANGES b/CHANGES
index f0bba520c253136c20ddd4248f06125851eebed7..a39958671f94e425a472ff0799bbdaaa56a2b796 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,10 @@
+4277.  [performance]   Improve performance of the RBT, the central zone
+                       datastructure: The aux hashtable was improved,
+                       hash function was updated to perform more
+                       uniform mapping, uppernode was added to
+                       dns_rbtnode, and other cleanups and performance
+                       improvements were made. [RT #41165]
+
 4276.  [protocol]      Add support for SMIMEA. [RT #40513]
 
 4275.  [performance]   Lazily initialize dns_compress->table only when
index 6799d3b2e298bd9f54468739ec40937101d7e81b..317674b39320b4992e89097c27df732239ad841b 100644 (file)
@@ -568,7 +568,7 @@ setcachestats(dns_db_t *db, isc_stats_t *stats) {
        return (dns_db_setcachestats(sampledb->rbtdb, stats));
 }
 
-static unsigned int
+static size_t
 hashsize(dns_db_t *db) {
        sampledb_t *sampledb = (sampledb_t *) db;
 
index 8d593231267489064c6d940a0d768cdd772b81fd..178d2469c3665c7de42e1eb52eefe2b74d02c75e 100644 (file)
@@ -472,8 +472,7 @@ finddbent(dns_acache_t *acache, dns_db_t *db, dbentry_t **dbentryp) {
         * The caller must be holding the acache lock.
         */
 
-       bucket = isc_hash_calc((const unsigned char *)&db,
-                              sizeof(db), ISC_TRUE) % DBBUCKETS;
+       bucket = isc_hash_function(&db, sizeof(db), ISC_TRUE, NULL) % DBBUCKETS;
 
        for (dbentry = ISC_LIST_HEAD(acache->dbbucket[bucket]);
             dbentry != NULL;
@@ -1264,8 +1263,7 @@ dns_acache_setdb(dns_acache_t *acache, dns_db_t *db) {
        dbentry->db = NULL;
        dns_db_attach(db, &dbentry->db);
 
-       bucket = isc_hash_calc((const unsigned char *)&db,
-                              sizeof(db), ISC_TRUE) % DBBUCKETS;
+       bucket = isc_hash_function(&db, sizeof(db), ISC_TRUE, NULL) % DBBUCKETS;
 
        ISC_LIST_APPEND(acache->dbbucket[bucket], dbentry, link);
 
@@ -1353,8 +1351,8 @@ dns_acache_putdb(dns_acache_t *acache, dns_db_t *db) {
        INSIST(ISC_LIST_EMPTY(dbentry->originlist) &&
               ISC_LIST_EMPTY(dbentry->referlist));
 
-       bucket = isc_hash_calc((const unsigned char *)&db,
-                              sizeof(db), ISC_TRUE) % DBBUCKETS;
+       bucket = isc_hash_function(&db, sizeof(db), ISC_TRUE, NULL) % DBBUCKETS;
+
        ISC_LIST_UNLINK(acache->dbbucket[bucket], dbentry, link);
        dns_db_detach(&dbentry->db);
 
index 22ffdb54a61ac9f0cc1fa9cab7f12ac7cbd3ca8d..83da390b47647f64e0b30b7e9770b58c29628f56 100644 (file)
@@ -1406,7 +1406,7 @@ dns_cache_dumpstats(dns_cache_t *cache, FILE *fp) {
                "cache records deleted due to TTL expiration");
        fprintf(fp, "%20u %s\n", dns_db_nodecount(cache->db),
                "cache database nodes");
-       fprintf(fp, "%20u %s\n", dns_db_hashsize(cache->db),
+       fprintf(fp, "%20zu %s\n", dns_db_hashsize(cache->db),
                "cache database hash buckets");
 
        fprintf(fp, "%20u %s\n", (unsigned int) isc_mem_total(cache->mctx),
index 7e4f35719b7199c640b946f03d881eaecd25f28e..e59abacbff8a24c71b2738a2bf8b56e8596b321a 100644 (file)
@@ -875,12 +875,12 @@ dns_db_nodecount(dns_db_t *db) {
        return ((db->methods->nodecount)(db));
 }
 
-unsigned int
+size_t
 dns_db_hashsize(dns_db_t *db) {
        REQUIRE(DNS_DB_VALID(db));
 
        if (db->methods->hashsize == NULL)
-               return (ISC_R_NOTIMPLEMENTED);
+               return (0);
 
        return ((db->methods->hashsize)(db));
 }
index 4679d639ee8716d9cdb2693e2a9abd09914c0bbb..9715249f9aa64f84f6bc8404490662c6b006ad58 100644 (file)
@@ -194,7 +194,7 @@ typedef struct dns_dbmethods {
                                   dns_rdataset_t *rdataset,
                                   dns_rdataset_t *sigrdataset);
        isc_result_t    (*setcachestats)(dns_db_t *db, isc_stats_t *stats);
-       unsigned int    (*hashsize)(dns_db_t *db);
+       size_t          (*hashsize)(dns_db_t *db);
 } dns_dbmethods_t;
 
 typedef isc_result_t
@@ -1380,7 +1380,7 @@ dns_db_nodecount(dns_db_t *db);
  * \li The number of nodes in the database
  */
 
-unsigned int
+size_t
 dns_db_hashsize(dns_db_t *db);
 /*%<
  * For database implementations using a hash table, report the
@@ -1392,7 +1392,7 @@ dns_db_hashsize(dns_db_t *db);
  *
  * Returns:
  * \li The number of buckets in the database's hash table, or
- *      ISC_R_NOTIMPLEMENTED.
+ *      0 if not implemented.
  */
 
 void
index 19f2d4d48061c5004303d71696d311cb6b88d1ad..013cd74eb5fd72751b56eb57f1b5372db5bc0f3c 100644 (file)
@@ -88,6 +88,7 @@ struct dns_rbtnode {
        dns_rbtnode_t *right;
        dns_rbtnode_t *down;
 #ifdef DNS_RBT_USEHASH
+       dns_rbtnode_t *uppernode;
        dns_rbtnode_t *hashnext;
 #endif
 
@@ -657,7 +658,7 @@ dns_rbt_nodecount(dns_rbt_t *rbt);
  * \li  rbt is a valid rbt manager.
  */
 
-unsigned int
+size_t
 dns_rbt_hashsize(dns_rbt_t *rbt);
 /*%<
  * Obtain the current number of buckets in the 'rbt' hash table.
index 6e1726ab44b081adefadd40aecbba415baf46f29..80ebe5a76770f099155f5a826d30d98e44f14be6 100644 (file)
@@ -29,6 +29,7 @@
 #include <isc/mem.h>
 #include <isc/once.h>
 #include <isc/print.h>
+#include <isc/random.h>
 #include <isc/string.h>
 #include <isc/thread.h>
 #include <isc/util.h>
@@ -478,42 +479,10 @@ dns_name_internalwildcard(const dns_name_t *name) {
        return (ISC_FALSE);
 }
 
-static inline unsigned int
-name_hash(dns_name_t *name, isc_boolean_t case_sensitive) {
-       unsigned int length;
-       const unsigned char *s;
-       unsigned int h = 0;
-       unsigned char c;
-
-       length = name->length;
-       if (length > 16)
-               length = 16;
-
-       /*
-        * This hash function is similar to the one Ousterhout
-        * uses in Tcl.
-        */
-       s = name->ndata;
-       if (case_sensitive) {
-               while (length > 0) {
-                       h += ( h << 3 ) + *s;
-                       s++;
-                       length--;
-               }
-       } else {
-               while (length > 0) {
-                       c = maptolower[*s];
-                       h += ( h << 3 ) + c;
-                       s++;
-                       length--;
-               }
-       }
-
-       return (h);
-}
-
 unsigned int
 dns_name_hash(dns_name_t *name, isc_boolean_t case_sensitive) {
+       unsigned int length;
+
        /*
         * Provide a hash value for 'name'.
         */
@@ -522,7 +491,12 @@ dns_name_hash(dns_name_t *name, isc_boolean_t case_sensitive) {
        if (name->labels == 0)
                return (0);
 
-       return (name_hash(name, case_sensitive));
+       length = name->length;
+       if (length > 16)
+               length = 16;
+
+       return (isc_hash_function_reverse(name->ndata, length,
+                                         case_sensitive, NULL));
 }
 
 unsigned int
@@ -535,19 +509,17 @@ dns_name_fullhash(dns_name_t *name, isc_boolean_t case_sensitive) {
        if (name->labels == 0)
                return (0);
 
-       return (isc_hash_calc((const unsigned char *)name->ndata,
-                             name->length, case_sensitive));
+       return (isc_hash_function_reverse(name->ndata, name->length,
+                                         case_sensitive, NULL));
 }
 
 unsigned int
 dns_fullname_hash(dns_name_t *name, isc_boolean_t case_sensitive) {
        /*
         * This function was deprecated due to the breakage of the name space
-        * convention.  We only keep this internally to provide binary backward
+        * convention.  We only keep this internally to provide binary backward
         * compatibility.
         */
-       REQUIRE(VALID_NAME(name));
-
        return (dns_name_fullhash(name, case_sensitive));
 }
 
@@ -567,7 +539,8 @@ dns_name_hashbylabel(dns_name_t *name, isc_boolean_t case_sensitive) {
        if (name->labels == 0)
                return (0);
        else if (name->labels == 1)
-               return (name_hash(name, case_sensitive));
+               return (isc_hash_function_reverse(name->ndata, name->length,
+                                                 case_sensitive, NULL));
 
        SETUP_OFFSETS(name, offsets, odata);
        DNS_NAME_INIT(&tname, NULL);
@@ -579,7 +552,8 @@ dns_name_hashbylabel(dns_name_t *name, isc_boolean_t case_sensitive) {
                        tname.length = name->length - offsets[i];
                else
                        tname.length = offsets[i + 1] - offsets[i];
-               h += name_hash(&tname, case_sensitive);
+               h += isc_hash_function_reverse(tname.ndata, tname.length,
+                                              case_sensitive, NULL);
        }
 
        return (h);
@@ -637,12 +611,15 @@ dns_name_fullcompare(const dns_name_t *name1, const dns_name_t *name2,
                ldiff = l1 - l2;
        }
 
+       offsets1 += l1;
+       offsets2 += l2;
+
        while (l > 0) {
                l--;
-               l1--;
-               l2--;
-               label1 = &name1->ndata[offsets1[l1]];
-               label2 = &name2->ndata[offsets2[l2]];
+               offsets1--;
+               offsets2--;
+               label1 = &name1->ndata[*offsets1];
+               label2 = &name2->ndata[*offsets2];
                count1 = *label1++;
                count2 = *label2++;
 
@@ -658,16 +635,41 @@ dns_name_fullcompare(const dns_name_t *name1, const dns_name_t *name2,
                else
                        count = count2;
 
-               while (count > 0) {
-                       chdiff = (int)maptolower[*label1] -
-                           (int)maptolower[*label2];
+               while (count > 3) {
+                       chdiff = (int)maptolower[label1[0]] -
+                                (int)maptolower[label2[0]];
+                       if (chdiff != 0) {
+                               *orderp = chdiff;
+                               goto done;
+                       }
+                       chdiff = (int)maptolower[label1[1]] -
+                                (int)maptolower[label2[1]];
+                       if (chdiff != 0) {
+                               *orderp = chdiff;
+                               goto done;
+                       }
+                       chdiff = (int)maptolower[label1[2]] -
+                                (int)maptolower[label2[2]];
+                       if (chdiff != 0) {
+                               *orderp = chdiff;
+                               goto done;
+                       }
+                       chdiff = (int)maptolower[label1[3]] -
+                                (int)maptolower[label2[3]];
+                       if (chdiff != 0) {
+                               *orderp = chdiff;
+                               goto done;
+                       }
+                       count -= 4;
+                       label1 += 4;
+                       label2 += 4;
+               }
+               while (count-- > 0) {
+                       chdiff = (int)maptolower[*label1++] - (int)maptolower[*label2++];
                        if (chdiff != 0) {
                                *orderp = chdiff;
                                goto done;
                        }
-                       count--;
-                       label1++;
-                       label2++;
                }
                if (cdiff != 0) {
                        *orderp = cdiff;
@@ -683,11 +685,12 @@ dns_name_fullcompare(const dns_name_t *name1, const dns_name_t *name2,
                namereln = dns_namereln_subdomain;
        else
                namereln = dns_namereln_equal;
+       *nlabelsp = nlabels;
+       return (namereln);
 
  done:
        *nlabelsp = nlabels;
-
-       if (nlabels > 0 && namereln == dns_namereln_none)
+       if (nlabels > 0)
                namereln = dns_namereln_commonancestor;
 
        return (namereln);
@@ -749,16 +752,31 @@ dns_name_equal(const dns_name_t *name1, const dns_name_t *name2) {
 
        label1 = name1->ndata;
        label2 = name2->ndata;
-       while (l > 0) {
-               l--;
+       while (l-- > 0) {
                count = *label1++;
                if (count != *label2++)
                        return (ISC_FALSE);
 
                INSIST(count <= 63); /* no bitstring support */
 
-               while (count > 0) {
-                       count--;
+               while (count > 3) {
+                       c = maptolower[label1[0]];
+                       if (c != maptolower[label2[0]])
+                               return (ISC_FALSE);
+                       c = maptolower[label1[1]];
+                       if (c != maptolower[label2[1]])
+                               return (ISC_FALSE);
+                       c = maptolower[label1[2]];
+                       if (c != maptolower[label2[2]])
+                               return (ISC_FALSE);
+                       c = maptolower[label1[3]];
+                       if (c != maptolower[label2[3]])
+                               return (ISC_FALSE);
+                       count -= 4;
+                       label1 += 4;
+                       label2 += 4;
+               }
+               while (count-- > 0) {
                        c = maptolower[*label1++];
                        if (c != maptolower[*label2++])
                                return (ISC_FALSE);
index 10b8c498b4c9a1f60e85de70a4bfa34e9899f5f0..a414e678bfdcf3c95f466e28231fae3780df2bbf 100644 (file)
 #endif
 
 struct dns_rbt {
-       unsigned int            magic;
-       isc_mem_t *             mctx;
-       dns_rbtnode_t *         root;
-       void                    (*data_deleter)(void *, void *);
-       void *                  deleter_arg;
-       unsigned int            nodecount;
-       unsigned int            hashsize;
-       dns_rbtnode_t **        hashtable;
-       void *                  mmap_location;
+       unsigned int            magic;
+       isc_mem_t *             mctx;
+       dns_rbtnode_t *         root;
+       void                    (*data_deleter)(void *, void *);
+       void *                  deleter_arg;
+       unsigned int            nodecount;
+       size_t                  hashsize;
+       dns_rbtnode_t **        hashtable;
+       void *                  mmap_location;
 };
 
 #define RED 0
@@ -208,6 +208,9 @@ getdata(dns_rbtnode_t *node, file_header_t *header) {
 #define LEFT(node)              ((node)->left)
 #define RIGHT(node)             ((node)->right)
 #define DOWN(node)              ((node)->down)
+#ifdef DNS_RBT_USEHASH
+#define UPPERNODE(node)         ((node)->uppernode)
+#endif /* DNS_RBT_USEHASH */
 #define DATA(node)              ((node)->data)
 #define IS_EMPTY(node)          ((node)->data == NULL)
 #define HASHNEXT(node)          ((node)->hashnext)
@@ -345,6 +348,39 @@ hexdump(const char *desc, unsigned char *data, size_t size) {
 }
 #endif /* DEBUG */
 
+#ifdef DNS_RBT_USEHASH
+
+/* Upper node is the parent of the root of the passed node's
+ * subtree. The passed node must not be NULL.
+ */
+static inline dns_rbtnode_t *
+get_upper_node(dns_rbtnode_t *node) {
+       return (UPPERNODE(node));
+}
+
+static void
+fixup_uppernodes_helper(dns_rbtnode_t *node, dns_rbtnode_t *uppernode) {
+       if (node == NULL)
+               return;
+
+       UPPERNODE(node) = uppernode;
+
+       fixup_uppernodes_helper(LEFT(node), uppernode);
+       fixup_uppernodes_helper(RIGHT(node), uppernode);
+       fixup_uppernodes_helper(DOWN(node), node);
+}
+
+/*
+ * This function is used to fixup uppernode members of all dns_rbtnodes
+ * after deserialization.
+ */
+static void
+fixup_uppernodes(dns_rbt_t *rbt) {
+       fixup_uppernodes_helper(rbt->root, NULL);
+}
+
+#else
+
 /* The passed node must not be NULL. */
 static inline dns_rbtnode_t *
 get_subtree_root(dns_rbtnode_t *node) {
@@ -370,6 +406,8 @@ get_upper_node(dns_rbtnode_t *node) {
        return (PARENT(root));
 }
 
+#endif /* DNS_RBT_USEHASH */
+
 size_t
 dns__rbtnode_getdistance(dns_rbtnode_t *node) {
        size_t nodes = 1;
@@ -421,11 +459,9 @@ treefix(dns_rbt_t *rbt, void *base, size_t size,
        dns_rbtdatafixer_t datafixer, void *fixer_arg,
        isc_uint64_t *crc);
 
-static isc_result_t
-deletetree(dns_rbt_t *rbt, dns_rbtnode_t *node);
-
 static void
-deletetreeflat(dns_rbt_t *rbt, unsigned int quantum, dns_rbtnode_t **nodep);
+deletetreeflat(dns_rbt_t *rbt, unsigned int quantum, isc_boolean_t unhash,
+              dns_rbtnode_t **nodep);
 
 static void
 printnodename(dns_rbtnode_t *node, isc_boolean_t quoted, FILE *f);
@@ -898,13 +934,19 @@ dns_rbt_deserialize_tree(void *base_address, size_t filesize,
                goto cleanup;
        }
 
+       if (header->nodecount != rbt->nodecount) {
+               result = ISC_R_INVALIDFILE;
+               goto cleanup;
+       }
+
+#ifdef DNS_RBT_USEHASH
+       fixup_uppernodes(rbt);
+#endif /* DNS_RBT_USEHASH */
+
        *rbtp = rbt;
        if (originp != NULL)
                *originp = rbt->root;
 
-       if (header->nodecount != rbt->nodecount)
-               result = ISC_R_INVALIDFILE;
-
  cleanup:
        if (result != ISC_R_SUCCESS && rbt != NULL) {
                rbt->root = NULL;
@@ -976,7 +1018,7 @@ dns_rbt_destroy2(dns_rbt_t **rbtp, unsigned int quantum) {
 
        rbt = *rbtp;
 
-       deletetreeflat(rbt, quantum, &rbt->root);
+       deletetreeflat(rbt, quantum, ISC_FALSE, &rbt->root);
        if (rbt->root != NULL)
                return (ISC_R_QUOTA);
 
@@ -1003,7 +1045,7 @@ dns_rbt_nodecount(dns_rbt_t *rbt) {
        return (rbt->nodecount);
 }
 
-unsigned int
+size_t
 dns_rbt_hashsize(dns_rbt_t *rbt) {
 
        REQUIRE(VALID_RBT(rbt));
@@ -1098,6 +1140,9 @@ dns_rbt_addnode(dns_rbt_t *rbt, dns_name_t *name, dns_rbtnode_t **nodep) {
                if (result == ISC_R_SUCCESS) {
                        rbt->nodecount++;
                        new_current->is_root = 1;
+#ifdef DNS_RBT_USEHASH
+                       UPPERNODE(new_current) = NULL;
+#endif /* DNS_RBT_USEHASH */
                        rbt->root = new_current;
                        *nodep = new_current;
                        hash_node(rbt, new_current, name);
@@ -1277,7 +1322,10 @@ dns_rbt_addnode(dns_rbt_t *rbt, dns_name_t *name, dns_rbtnode_t **nodep) {
                                PARENT(current) = new_current;
                                DOWN(new_current) = current;
                                root = &DOWN(new_current);
-
+#ifdef DNS_RBT_USEHASH
+                               UPPERNODE(new_current) = UPPERNODE(current);
+                               UPPERNODE(current) = new_current;
+#endif /* DNS_RBT_USEHASH */
                                ADD_LEVEL(&chain, new_current);
 
                                LEFT(current) = NULL;
@@ -1334,6 +1382,12 @@ dns_rbt_addnode(dns_rbt_t *rbt, dns_name_t *name, dns_rbtnode_t **nodep) {
                result = create_node(rbt->mctx, add_name, &new_current);
 
        if (result == ISC_R_SUCCESS) {
+#ifdef DNS_RBT_USEHASH
+               if (*root == NULL)
+                       UPPERNODE(new_current) = current;
+               else
+                       UPPERNODE(new_current) = PARENT(*root);
+#endif /* DNS_RBT_USEHASH */
                addonlevel(new_current, current, order, root);
                rbt->nodecount++;
                *nodep = new_current;
@@ -1475,12 +1529,6 @@ dns_rbt_findnode(dns_rbt_t *rbt, dns_name_t *name, dns_name_t *foundname,
                        unsigned int tlabels = 1;
                        unsigned int hash;
 
-                       /*
-                        * If there is no hash table, hashing can't be done.
-                        */
-                       if (rbt->hashtable == NULL)
-                               goto nohash;
-
                        /*
                         * The case of current != current_root, that
                         * means a left or right pointer was followed,
@@ -1495,7 +1543,7 @@ dns_rbt_findnode(dns_rbt_t *rbt, dns_name_t *name, dns_name_t *foundname,
 
                        /*
                         * current_root is the root of the current level, so
-                        * it's parent is the same as it's "up" pointer.
+                        * its parent is the same as its "up" pointer.
                         */
                        up_current = PARENT(current_root);
                        dns_name_init(&hash_name, NULL);
@@ -1579,8 +1627,8 @@ dns_rbt_findnode(dns_rbt_t *rbt, dns_name_t *name, dns_name_t *foundname,
                        current = NULL;
                        continue;
 
-               nohash:
-#endif /* DNS_RBT_USEHASH */
+#else /* DNS_RBT_USEHASH */
+
                        /*
                         * Standard binary search tree movement.
                         */
@@ -1589,6 +1637,8 @@ dns_rbt_findnode(dns_rbt_t *rbt, dns_name_t *name, dns_name_t *foundname,
                        else
                                current = RIGHT(current);
 
+#endif /* DNS_RBT_USEHASH */
+
                } else {
                        /*
                         * The names have some common suffix labels.
@@ -2019,10 +2069,10 @@ dns_rbt_deletenode(dns_rbt_t *rbt, dns_rbtnode_t *node, isc_boolean_t recurse)
        INSIST(rbt->nodecount != 0);
 
        if (DOWN(node) != NULL) {
-               if (recurse)
-                       RUNTIME_CHECK(deletetree(rbt, DOWN(node))
-                                     == ISC_R_SUCCESS);
-               else {
+               if (recurse) {
+                       PARENT(DOWN(node)) = NULL;
+                       deletetreeflat(rbt, 0, ISC_TRUE, &DOWN(node));
+               else {
                        if (DATA(node) != NULL && rbt->data_deleter != NULL)
                                rbt->data_deleter(DATA(node), rbt->deleter_arg);
                        DATA(node) = NULL;
@@ -2034,6 +2084,7 @@ dns_rbt_deletenode(dns_rbt_t *rbt, dns_rbtnode_t *node, isc_boolean_t recurse)
                         * by itself on a single level, so join_nodes() could
                         * be used to collapse the tree (with all the caveats
                         * of the comment at the start of this function).
+                        * But join_nodes() function has now been removed.
                         */
                        return (ISC_R_SUCCESS);
                }
@@ -2269,12 +2320,14 @@ rehash(dns_rbt_t *rbt, unsigned int newcount) {
        unsigned int oldsize;
        dns_rbtnode_t **oldtable;
        dns_rbtnode_t *node;
+       dns_rbtnode_t *nextnode;
        unsigned int hash;
        unsigned int i;
 
        oldsize = rbt->hashsize;
        oldtable = rbt->hashtable;
        do {
+               INSIST((rbt->hashsize * 2 + 1) > rbt->hashsize);
                rbt->hashsize = rbt->hashsize * 2 + 1;
        } while (newcount >= (rbt->hashsize * 3));
        rbt->hashtable = isc_mem_get(rbt->mctx,
@@ -2285,19 +2338,15 @@ rehash(dns_rbt_t *rbt, unsigned int newcount) {
                return;
        }
 
-       INSIST(rbt->hashsize > 0);
-
        for (i = 0; i < rbt->hashsize; i++)
                rbt->hashtable[i] = NULL;
 
        for (i = 0; i < oldsize; i++) {
-               node = oldtable[i];
-               while (node != NULL) {
+               for (node = oldtable[i]; node != NULL; node = nextnode) {
                        hash = HASHVAL(node) % rbt->hashsize;
-                       oldtable[i] = HASHNEXT(node);
+                       nextnode = HASHNEXT(node);
                        HASHNEXT(node) = rbt->hashtable[hash];
                        rbt->hashtable[hash] = node;
-                       node = oldtable[i];
                }
        }
 
@@ -2321,19 +2370,17 @@ unhash_node(dns_rbt_t *rbt, dns_rbtnode_t *node) {
 
        REQUIRE(DNS_RBTNODE_VALID(node));
 
-       if (rbt->hashtable != NULL) {
-               bucket = HASHVAL(node) % rbt->hashsize;
-               bucket_node = rbt->hashtable[bucket];
+       bucket = HASHVAL(node) % rbt->hashsize;
+       bucket_node = rbt->hashtable[bucket];
 
-               if (bucket_node == node)
-                       rbt->hashtable[bucket] = HASHNEXT(node);
-               else {
-                       while (HASHNEXT(bucket_node) != node) {
-                               INSIST(HASHNEXT(bucket_node) != NULL);
-                               bucket_node = HASHNEXT(bucket_node);
-                       }
-                       HASHNEXT(bucket_node) = HASHNEXT(node);
+       if (bucket_node == node) {
+               rbt->hashtable[bucket] = HASHNEXT(node);
+       } else {
+               while (HASHNEXT(bucket_node) != node) {
+                       INSIST(HASHNEXT(bucket_node) != NULL);
+                       bucket_node = HASHNEXT(bucket_node);
                }
+               HASHNEXT(bucket_node) = HASHNEXT(node);
        }
 }
 #endif /* DNS_RBT_USEHASH */
@@ -2747,64 +2794,6 @@ deletefromlevel(dns_rbtnode_t *delete, dns_rbtnode_t **rootp) {
        }
 }
 
-/*
- * This should only be used on the root of a tree, because no color fixup
- * is done at all.
- *
- * NOTE: No root pointer maintenance is done, because the function is only
- * used for two cases:
- * + deleting everything DOWN from a node that is itself being deleted, and
- * + deleting the entire tree of trees from dns_rbt_destroy.
- * In each case, the root pointer is no longer relevant, so there
- * is no need for a root parameter to this function.
- *
- * If the function is ever intended to be used to delete something where
- * a pointer needs to be told that this tree no longer exists,
- * this function would need to adjusted accordingly.
- */
-static isc_result_t
-deletetree(dns_rbt_t *rbt, dns_rbtnode_t *node) {
-       isc_result_t result = ISC_R_SUCCESS;
-
-       REQUIRE(VALID_RBT(rbt));
-
-       if (node == NULL)
-               return (result);
-
-       if (LEFT(node) != NULL) {
-               result = deletetree(rbt, LEFT(node));
-               if (result != ISC_R_SUCCESS)
-                       goto done;
-               LEFT(node) = NULL;
-       }
-       if (RIGHT(node) != NULL) {
-               result = deletetree(rbt, RIGHT(node));
-               if (result != ISC_R_SUCCESS)
-                       goto done;
-               RIGHT(node) = NULL;
-       }
-       if (DOWN(node) != NULL) {
-               result = deletetree(rbt, DOWN(node));
-               if (result != ISC_R_SUCCESS)
-                       goto done;
-               DOWN(node) = NULL;
-       }
- done:
-       if (result != ISC_R_SUCCESS)
-               return (result);
-
-       if (DATA(node) != NULL && rbt->data_deleter != NULL)
-               rbt->data_deleter(DATA(node), rbt->deleter_arg);
-
-       unhash_node(rbt, node);
-#if DNS_RBT_USEMAGIC
-       node->magic = 0;
-#endif
-
-       freenode(rbt, &node);
-       return (result);
-}
-
 static void
 freenode(dns_rbt_t *rbt, dns_rbtnode_t **nodep) {
        dns_rbtnode_t *node = *nodep;
@@ -2818,57 +2807,55 @@ freenode(dns_rbt_t *rbt, dns_rbtnode_t **nodep) {
 }
 
 static void
-deletetreeflat(dns_rbt_t *rbt, unsigned int quantum, dns_rbtnode_t **nodep) {
-       dns_rbtnode_t *parent;
-       dns_rbtnode_t *node = *nodep;
-
-       REQUIRE(VALID_RBT(rbt));
-
- again:
-       if (node == NULL) {
-               *nodep = NULL;
-               return;
-       }
-
- traverse:
-       if (LEFT(node) != NULL) {
-               node = LEFT(node);
-               goto traverse;
-       }
-       if (DOWN(node) != NULL) {
-               node = DOWN(node);
-               goto traverse;
-       }
+deletetreeflat(dns_rbt_t *rbt, unsigned int quantum, isc_boolean_t unhash,
+              dns_rbtnode_t **nodep)
+{
+       dns_rbtnode_t *root = *nodep;
 
-       if (DATA(node) != NULL && rbt->data_deleter != NULL)
-               rbt->data_deleter(DATA(node), rbt->deleter_arg);
+       while (root != NULL) {
+               /*
+                * If there is a left, right or down node, walk into it
+                * and iterate.
+                */
+               if (LEFT(root) != NULL) {
+                       dns_rbtnode_t *node = root;
+                       root = LEFT(root);
+                       LEFT(node) = NULL;
+               } else if (RIGHT(root) != NULL) {
+                       dns_rbtnode_t *node = root;
+                       root = RIGHT(root);
+                       RIGHT(node) = NULL;
+               } else if (DOWN(root) != NULL) {
+                       dns_rbtnode_t *node = root;
+                       root = DOWN(root);
+                       DOWN(node) = NULL;
+               } else {
+                       /*
+                        * There are no left, right or down nodes, so we
+                        * can free this one and go back to its parent.
+                        */
+                       dns_rbtnode_t *node = root;
+                       root = PARENT(root);
 
-       /*
-        * Note: we don't call unhash_node() here as we are destroying
-        * the complete rbt tree.
-        */
+                       if (DATA(node) != NULL && rbt->data_deleter != NULL)
+                               rbt->data_deleter(DATA(node),
+                                                 rbt->deleter_arg);
+                       if (unhash)
+                               unhash_node(rbt, node);
+                       /*
+                        * Note: we don't call unhash_node() here as we
+                        * are destroying the complete RBT tree.
+                        */
 #if DNS_RBT_USEMAGIC
-       node->magic = 0;
+                       node->magic = 0;
 #endif
-       parent = PARENT(node);
-       if (RIGHT(node) != NULL)
-               PARENT(RIGHT(node)) = parent;
-       if (parent != NULL) {
-               if (LEFT(parent) == node)
-                       LEFT(parent) = RIGHT(node);
-               else if (DOWN(parent) == node)
-                       DOWN(parent) = RIGHT(node);
-       } else
-               parent = RIGHT(node);
-
-       freenode(rbt, &node);
-
-       node = parent;
-       if (quantum != 0 && --quantum == 0) {
-               *nodep = node;
-               return;
+                       freenode(rbt, &node);
+                       if (quantum != 0 && --quantum == 0)
+                               break;
+               }
        }
-       goto again;
+
+       *nodep = root;
 }
 
 static size_t
index 0cd1e0e96026a1e7489aa2327b76c47d31a575ef..f39c76feed42e5569dd884e5cea0c796d57a374c 100644 (file)
@@ -7729,20 +7729,20 @@ nodecount(dns_db_t *db) {
        return (count);
 }
 
-static unsigned int
+static size_t
 hashsize(dns_db_t *db) {
        dns_rbtdb_t *rbtdb;
-       unsigned int count;
+       size_t size;
 
        rbtdb = (dns_rbtdb_t *)db;
 
        REQUIRE(VALID_RBTDB(rbtdb));
 
        RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
-       count = dns_rbt_hashsize(rbtdb->tree);
+       size = dns_rbt_hashsize(rbtdb->tree);
        RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
 
-       return (count);
+       return (size);
 }
 
 static void
index bc08e2c9ba279afdbaaaee80c78b36da10d4fa2f..577f0ab4debda5665a40fc3aa7efd1b6264302bf 100644 (file)
@@ -57,6 +57,7 @@ isc_socketmgr_t *socketmgr = NULL;
 dns_zonemgr_t *zonemgr = NULL;
 isc_boolean_t app_running = ISC_FALSE;
 int ncpus;
+isc_boolean_t debug_mem_record = ISC_TRUE;
 
 static isc_boolean_t hash_active = ISC_FALSE, dst_active = ISC_FALSE;
 
@@ -115,7 +116,8 @@ dns_test_begin(FILE *logfile, isc_boolean_t start_managers) {
 
        if (start_managers)
                CHECK(isc_app_start());
-       isc_mem_debugging |= ISC_MEM_DEBUGRECORD;
+       if (debug_mem_record)
+               isc_mem_debugging |= ISC_MEM_DEBUGRECORD;
        CHECK(isc_mem_create(0, 0, &mctx));
        CHECK(isc_entropy_create(mctx, &ectx));
 
index ba749738af68a117b8bb816a26a1c671d0c6cf19..679a54472923415c633ecb6550afd5aee36dce56 100644 (file)
@@ -50,6 +50,7 @@ extern isc_socketmgr_t *socketmgr;
 extern dns_zonemgr_t *zonemgr;
 extern isc_boolean_t app_running;
 extern int ncpus;
+extern isc_boolean_t debug_mem_record;
 
 isc_result_t
 dns_test_begin(FILE *logfile, isc_boolean_t create_managers);
index daf9419776acf10ef988f352b71226ec634fe115..f2e054c746f790eeb7342d5eaaa33712644b8cc0 100644 (file)
@@ -50,6 +50,7 @@
 #include <isc/timer.h>
 #include <isc/util.h>
 #include <isc/print.h>
+#include <isc/time.h>
 
 #include <dns/log.h>
 #include <dns/name.h>
@@ -58,6 +59,8 @@
 #include <dst/dst.h>
 
 #include <ctype.h>
+#include <stdlib.h>
+#include <time.h>
 
 typedef struct {
        dns_rbt_t *rbt;
@@ -1305,6 +1308,131 @@ ATF_TC_BODY(rbt_insert_and_remove, tc) {
        dns_test_end();
 }
 
+ATF_TC(benchmark);
+ATF_TC_HEAD(benchmark, tc) {
+       atf_tc_set_md_var(tc, "descr", "Benchmark RBT implementation");
+}
+
+static dns_fixedname_t *fnames;
+static dns_name_t **names;
+static int *values;
+
+static void *
+find_thread(void *arg) {
+       dns_rbt_t *mytree;
+       isc_result_t result;
+       dns_rbtnode_t *node;
+       unsigned int j, i;
+       unsigned int start = 0;
+
+       mytree = (dns_rbt_t *) arg;
+       while (start == 0)
+               start = random() % 4000000;
+
+       /* Query 32 million random names from it in each thread */
+       for (j = 0; j < 8; j++) {
+               for (i = start; i != start - 1; i = (i + 1) % 4000000) {
+                       node = NULL;
+                       result = dns_rbt_findnode(mytree, names[i], NULL,
+                                                 &node, NULL,
+                                                 DNS_RBTFIND_EMPTYDATA,
+                                                 NULL, NULL);
+                       ATF_CHECK_EQ(result, ISC_R_SUCCESS);
+                       ATF_REQUIRE(node != NULL);
+                       ATF_CHECK_EQ(values[i], (intptr_t) node->data);
+               }
+       }
+
+       return (NULL);
+}
+
+ATF_TC_BODY(benchmark, tc) {
+       isc_result_t result;
+       char namestr[sizeof("name18446744073709551616.example.org.")];
+       unsigned int r;
+       dns_rbt_t *mytree;
+       dns_rbtnode_t *node;
+       unsigned int i;
+       unsigned int maxvalue = 1000000;
+       isc_time_t ts1, ts2;
+       double t;
+       unsigned int nthreads;
+       pthread_t threads[32];
+
+       UNUSED(tc);
+
+       srandom(time(NULL));
+
+       debug_mem_record = ISC_FALSE;
+
+       result = dns_test_begin(NULL, ISC_TRUE);
+       ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+       fnames = (dns_fixedname_t *) malloc(4000000 * sizeof(dns_fixedname_t));
+       names = (dns_name_t **) malloc(4000000 * sizeof(dns_name_t *));
+       values = (int *) malloc(4000000 * sizeof(int));
+
+       for (i = 0; i < 4000000; i++) {
+                 r = ((unsigned long) random()) % maxvalue;
+                 snprintf(namestr, sizeof(namestr), "name%u.example.org.", r);
+                 build_name_from_str(namestr, &fnames[i]);
+                 names[i] = dns_fixedname_name(&fnames[i]);
+                 values[i] = r;
+       }
+
+       /* Create a tree. */
+       mytree = NULL;
+       result = dns_rbt_create(mctx, NULL, NULL, &mytree);
+       ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+       /* Insert test data into the tree. */
+       for (i = 0; i < maxvalue; i++) {
+               snprintf(namestr, sizeof(namestr), "name%u.example.org.", i);
+               node = NULL;
+               result = insert_helper(mytree, namestr, &node);
+               ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+               node->data = (void *) (intptr_t) i;
+       }
+
+       result = isc_time_now(&ts1);
+       ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+       nthreads = ISC_MIN(isc_os_ncpus(), 32);
+       nthreads = ISC_MAX(nthreads, 1);
+       for (i = 0; i < nthreads; i++) {
+               int s;
+
+               s = pthread_create(&threads[i], NULL, find_thread, mytree);
+
+               ATF_REQUIRE(s == 0);
+       }
+
+       for (i = 0; i < nthreads; i++) {
+               int s;
+
+               s = pthread_join(threads[i], NULL);
+
+               ATF_REQUIRE(s == 0);
+       }
+
+       result = isc_time_now(&ts2);
+       ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
+
+       t = isc_time_microdiff(&ts2, &ts1);
+
+       printf("%u findnode calls, %f seconds, %f calls/second\n",
+              nthreads * 8 * 4000000, t / 1000000.0,
+              (nthreads * 8 * 4000000) / (t / 1000000.0));
+
+       free(values);
+       free(names);
+       free(fnames);
+
+       dns_rbt_destroy(&mytree);
+
+       dns_test_end();
+}
+
 /*
  * Main
  */
@@ -1318,6 +1446,7 @@ ATF_TP_ADD_TCS(tp) {
        ATF_TP_ADD_TC(tp, rbt_remove);
        ATF_TP_ADD_TC(tp, rbt_remove_empty);
        ATF_TP_ADD_TC(tp, rbt_insert_and_remove);
+       ATF_TP_ADD_TC(tp, benchmark);
 
        return (atf_no_error());
 }
index 00d869261d3cefe85b50501b56a89b134519120e..95b573db10cff7430f8d64cbdde9ededee88af48 100644 (file)
@@ -403,3 +403,142 @@ isc__hash_setvec(const isc_uint16_t *vec) {
                p[i] = vec[i];
        }
 }
+
+static unsigned int fnv_offset_basis;
+static isc_once_t fnv_once = ISC_ONCE_INIT;
+
+static void
+fnv_initialize(void) {
+       /*
+        * This function should not leave fnv_offset_basis set to
+        * 0. Also, after this function has been called, if it is called
+        * again, it should not change fnv_offset_basis.
+        */
+       while (fnv_offset_basis == 0) {
+               isc_random_get(&fnv_offset_basis);
+       }
+}
+
+unsigned int
+isc_hash_function(const void *data, size_t length,
+                 isc_boolean_t case_sensitive,
+                 unsigned int *previous_hashp)
+{
+       unsigned int hval;
+       const unsigned char *bp;
+       const unsigned char *be;
+
+       RUNTIME_CHECK(isc_once_do(&fnv_once, fnv_initialize) == ISC_R_SUCCESS);
+
+       hval = previous_hashp != NULL ? *previous_hashp : fnv_offset_basis;
+
+       bp = (const unsigned char *) data;
+       be = bp + length;
+
+       /*
+        * Fowler-Noll-Vo FNV-1a hash function.
+        *
+        * NOTE: A random fnv_offset_basis is used by default to avoid
+        * collision attacks as the hash function is reversible. This
+        * makes the mapping non-deterministic, but the distribution in
+        * the domain is still uniform.
+        */
+
+       if (case_sensitive) {
+               while (bp < be - 4) {
+                       hval ^= (unsigned int) bp[0];
+                       hval *= 16777619;
+                       hval ^= (unsigned int) bp[1];
+                       hval *= 16777619;
+                       hval ^= (unsigned int) bp[2];
+                       hval *= 16777619;
+                       hval ^= (unsigned int) bp[3];
+                       hval *= 16777619;
+                       bp += 4;
+               }
+               while (bp < be) {
+                       hval ^= (unsigned int) *bp++;
+                       hval *= 16777619;
+               }
+       } else {
+               while (bp < be - 4) {
+                       hval ^= (unsigned int) maptolower[bp[0]];
+                       hval *= 16777619;
+                       hval ^= (unsigned int) maptolower[bp[1]];
+                       hval *= 16777619;
+                       hval ^= (unsigned int) maptolower[bp[2]];
+                       hval *= 16777619;
+                       hval ^= (unsigned int) maptolower[bp[3]];
+                       hval *= 16777619;
+                       bp += 4;
+               }
+               while (bp < be) {
+                       hval ^= (unsigned int) maptolower[*bp++];
+                       hval *= 16777619;
+               }
+       }
+
+       return (hval);
+}
+
+unsigned int
+isc_hash_function_reverse(const void *data, size_t length,
+                         isc_boolean_t case_sensitive,
+                         unsigned int *previous_hashp)
+{
+       unsigned int hval;
+       const unsigned char *bp;
+       const unsigned char *be;
+
+       RUNTIME_CHECK(isc_once_do(&fnv_once, fnv_initialize) == ISC_R_SUCCESS);
+
+       hval = previous_hashp != NULL ? *previous_hashp : fnv_offset_basis;
+
+       bp = (const unsigned char *) data;
+       be = bp + length;
+
+       /*
+        * Fowler-Noll-Vo FNV-1a hash function.
+        *
+        * NOTE: A random fnv_offset_basis is used by default to avoid
+        * collision attacks as the hash function is reversible. This
+        * makes the mapping non-deterministic, but the distribution in
+        * the domain is still uniform.
+        */
+
+       if (case_sensitive) {
+               while (be >= bp + 4) {
+                       be -= 4;
+                       hval ^= (unsigned int) be[3];
+                       hval *= 16777619;
+                       hval ^= (unsigned int) be[2];
+                       hval *= 16777619;
+                       hval ^= (unsigned int) be[1];
+                       hval *= 16777619;
+                       hval ^= (unsigned int) be[0];
+                       hval *= 16777619;
+               }
+               while (--be >= bp) {
+                       hval ^= (unsigned int) *be;
+                       hval *= 16777619;
+               }
+       } else {
+               while (be >= bp + 4) {
+                       be -= 4;
+                       hval ^= (unsigned int) maptolower[be[3]];
+                       hval *= 16777619;
+                       hval ^= (unsigned int) maptolower[be[2]];
+                       hval *= 16777619;
+                       hval ^= (unsigned int) maptolower[be[1]];
+                       hval *= 16777619;
+                       hval ^= (unsigned int) maptolower[be[0]];
+                       hval *= 16777619;
+               }
+               while (--be >= bp) {
+                       hval ^= (unsigned int) maptolower[*be];
+                       hval *= 16777619;
+               }
+       }
+
+       return (hval);
+}
index a53677ece597a06ee16df671879eeb8e88e856e7..fe344a0159cbdbb0158470a9c18fbc67ae46a984 100644 (file)
@@ -198,6 +198,44 @@ isc__hash_setvec(const isc_uint16_t *vec);
  * doing before using this function.
  */
 
+unsigned int
+isc_hash_function(const void *data, size_t length,
+                 isc_boolean_t case_sensitive,
+                 unsigned int *previous_hashp);
+unsigned int
+isc_hash_function_reverse(const void *data, size_t length,
+                         isc_boolean_t case_sensitive,
+                         unsigned int *previous_hashp);
+/*!<
+ * \brief Calculate a hash over data.
+ *
+ * This hash function is useful for hashtables. The hash function is
+ * opaque and not important to the caller. The returned hash values are
+ * non-deterministic and will have different mapping every time a
+ * process using this library is run, but will have uniform
+ * distribution.
+ *
+ * isc_hash_function() calculates the hash from start to end over the
+ * input data. isc_hash_function_reverse() calculates the hash from the
+ * end to the start over the input data. The difference in order is
+ * useful in incremental hashing.
+ *
+ * This is a new variant of isc_hash_calc() and will supercede
+ * isc_hash_calc() eventually.
+ *
+ * 'data' is the data to be hashed.
+ *
+ * 'length' is the size of the data to be hashed.
+ *
+ * 'case_sensitive' specifies whether the hash key should be treated as
+ * case_sensitive values.  It should typically be ISC_FALSE if the hash key
+ * is a DNS name.
+ *
+ * 'previous_hashp' is a pointer to a previous hash value returned by
+ * this function. It can be used to perform incremental hashing. NULL
+ * must be passed during first calls.
+ */
+
 ISC_LANG_ENDDECLS
 
 #endif /* ISC_HASH_H */
index d6a5849ed5ad4282a3a42394d7a7861dd0d69cfc..eda89be72eac2f53074ae33539d82a0a4c2c4ee6 100644 (file)
@@ -205,7 +205,6 @@ isc_sockaddr_hash(const isc_sockaddr_t *sockaddr, isc_boolean_t address_only) {
        unsigned int length = 0;
        const unsigned char *s = NULL;
        unsigned int h = 0;
-       unsigned int g;
        unsigned int p = 0;
        const struct in6_addr *in6;
 
@@ -239,12 +238,9 @@ isc_sockaddr_hash(const isc_sockaddr_t *sockaddr, isc_boolean_t address_only) {
                p = 0;
        }
 
-       h = isc_hash_calc(s, length, ISC_TRUE);
-       if (!address_only) {
-               g = isc_hash_calc((const unsigned char *)&p, sizeof(p),
-                                 ISC_TRUE);
-               h = h ^ g; /* XXX: we should concatenate h and p first */
-       }
+       h = isc_hash_function(s, length, ISC_TRUE, NULL);
+       if (!address_only)
+               h = isc_hash_function(&p, sizeof(p), ISC_TRUE, &h);
 
        return (h);
 }
index d982c5db27565178003f03fffb8ea963d7ff8736..791856fd74e22fd4dcf4041496aa20288bfc6eb5 100644 (file)
@@ -261,6 +261,8 @@ isc_hash_ctxcreate
 isc_hash_ctxdetach
 isc_hash_ctxinit
 isc_hash_destroy
+isc_hash_function
+isc_hash_function_reverse
 isc_hash_init
 isc_heap_create
 isc_heap_decreased