Forráskód Böngészése

Add HASH_VALUE and HASH_FIND_BY_HASH_VALUE.

This can massively speed up client code that looks up the same key in
multiple hashes, or (for readability reasons) looks up the same key
multiple times in the same hash. If the client code can cache the
HASH_VALUE of the key and reuse it for the multiple lookups, it will
be a massive performance improvement.
Arthur O'Dwyer 9 éve
szülő
commit
57a22545ea
2 módosított fájl, 78 hozzáadás és 14 törlés
  1. 22 12
      doc/userguide.txt
  2. 56 2
      src/uthash.h

+ 22 - 12
doc/userguide.txt

@@ -1712,18 +1712,23 @@ than `hh`, or if your key's data type isn't `int` or `char[]`.
 .General macros
 [width="90%",cols="10m,30m",grid="none",options="header"]
 |===============================================================================
-|macro          | arguments
-|HASH_ADD       | (hh_name, head, keyfield_name, key_len, item_ptr)
-|HASH_ADD_KEYPTR| (hh_name, head, key_ptr, key_len, item_ptr)
-|HASH_REPLACE   | (hh_name, head, keyfield_name, key_len, item_ptr, replaced_item_ptr)
-|HASH_FIND      | (hh_name, head, key_ptr, key_len, item_ptr)
-|HASH_DELETE    | (hh_name, head, item_ptr)
-|HASH_SRT       | (hh_name, head, cmp)
-|HASH_CNT       | (hh_name, head)
-|HASH_CLEAR     | (hh_name, head)
-|HASH_SELECT    | (dst_hh_name, dst_head, src_hh_name, src_head, condition)
-|HASH_ITER      | (hh_name, head, item_ptr, tmp_item_ptr)
-|HASH_OVERHEAD  | (hh_name, head)
+|macro                        | arguments
+|HASH_ADD                     | (hh_name, head, keyfield_name, key_len, item_ptr)
+|HASH_ADD_BY_HASH_VALUE       | (hh_name, head, keyfield_name, key_len, key_hash, item_ptr)
+|HASH_ADD_KEYPTR              | (hh_name, head, key_ptr, key_len, item_ptr)
+|HASH_ADD_KEYPTR_BY_HASH_VALUE| (hh_name, head, key_ptr, key_len, key_hash, item_ptr)
+|HASH_REPLACE                 | (hh_name, head, keyfield_name, key_len, item_ptr, replaced_item_ptr)
+|HASH_REPLACE_BY_HASH_VALUE   | (hh_name, head, keyfield_name, key_len, key_hash, item_ptr, replaced_item_ptr)
+|HASH_FIND                    | (hh_name, head, key_ptr, key_len, item_ptr)
+|HASH_FIND_BY_HASH_VALUE      | (hh_name, head, key_ptr, key_len, key_hash, item_ptr)
+|HASH_DELETE                  | (hh_name, head, item_ptr)
+|HASH_VALUE                   | (key_ptr, key_len, key_hash)
+|HASH_SRT                     | (hh_name, head, cmp)
+|HASH_CNT                     | (hh_name, head)
+|HASH_CLEAR                   | (hh_name, head)
+|HASH_SELECT                  | (dst_hh_name, dst_head, src_hh_name, src_head, condition)
+|HASH_ITER                    | (hh_name, head, item_ptr, tmp_item_ptr)
+|HASH_OVERHEAD                | (hh_name, head)
 |===============================================================================
 
 [NOTE]
@@ -1752,6 +1757,11 @@ key_ptr::
     for `HASH_FIND`, this is a pointer to the key to look up in the hash
     (since it's a pointer, you can't directly pass a literal value here). For
     `HASH_ADD_KEYPTR`, this is the address of the key of the item being added.
+key_hash::
+    the hash value of the provided key. This is an input parameter for the
+    `..._BY_HASH_VALUE` macros, and an output parameter for `HASH_VALUE`.
+    Reusing a cached hash value can be a performance optimization if
+    you're going to do repeated lookups for the same key.
 item_ptr::
     pointer to the structure being added, deleted, or looked up, or the current
     pointer during iteration. This is an input parameter for `HASH_ADD` and

+ 56 - 2
src/uthash.h

@@ -103,6 +103,12 @@ typedef unsigned char uint8_t;
 /* calculate the element whose hash handle address is hhe */
 #define ELMT_FROM_HH(tbl,hhp) ((void*)(((char*)(hhp)) - ((tbl)->hho)))
 
+#define HASH_VALUE(keyptr,keylen,hashv)                                          \
+do {                                                                             \
+ unsigned _hf_bkt;                                                               \
+ HASH_FCN(keyptr,keylen,1,hashv,_hf_bkt);                                        \
+} while (0)
+
 #define HASH_FIND(hh,head,keyptr,keylen,out)                                     \
 do {                                                                             \
   out=NULL;                                                                      \
@@ -116,6 +122,19 @@ do {
   }                                                                              \
 } while (0)
 
+#define HASH_FIND_BY_HASH_VALUE(hh,head,keyptr,keylen,_hf_hashv,out)             \
+do {                                                                             \
+  out=NULL;                                                                      \
+  if (head != NULL) {                                                            \
+     unsigned _hf_bkt;                                                           \
+     HASH_TO_BKT(_hf_hashv, (head)->hh.tbl->num_buckets, _hf_bkt);               \
+     if (HASH_BLOOM_TEST((head)->hh.tbl, _hf_hashv) != 0) {                      \
+       HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ],  \
+                        keyptr,keylen,out);                                      \
+     }                                                                           \
+  }                                                                              \
+} while (0)
+
 #ifdef HASH_BLOOM
 #define HASH_BLOOM_BITLEN (1UL << HASH_BLOOM)
 #define HASH_BLOOM_BYTELEN (HASH_BLOOM_BITLEN/8UL) + (((HASH_BLOOM_BITLEN%8UL)!=0UL) ? 1UL : 0UL)
@@ -172,14 +191,24 @@ do {
 #define HASH_ADD(hh,head,fieldname,keylen_in,add)                                \
         HASH_ADD_KEYPTR(hh,head,&((add)->fieldname),keylen_in,add)
 
+#define HASH_ADD_BY_HASH_VALUE(hh,head,fieldname,keylen_in,hashval,add)          \
+        HASH_ADD_KEYPTR_BY_HASH_VALUE(hh,head,&((add)->fieldname),keylen_in,hashval,add)
+
 #define HASH_REPLACE(hh,head,fieldname,keylen_in,add,replaced)                   \
+do {                                                                             \
+  unsigned _hr_hashv;                                                            \
+  HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv);                         \
+  HASH_REPLACE_BY_HASH_VALUE(hh,head,fieldname,keylen_in,_hr_hashv,add,replaced);\
+} while(0)
+
+#define HASH_REPLACE_BY_HASH_VALUE(hh,head,fieldname,keylen_in,hashval,add,replaced) \
 do {                                                                             \
   replaced=NULL;                                                                 \
-  HASH_FIND(hh,head,&((add)->fieldname),keylen_in,replaced);                     \
+  HASH_FIND_BY_HASH_VALUE(hh,head,&((add)->fieldname),keylen_in,hashval,replaced); \
   if (replaced!=NULL) {                                                          \
      HASH_DELETE(hh,head,replaced);                                              \
   }                                                                              \
-  HASH_ADD(hh,head,fieldname,keylen_in,add);                                     \
+  HASH_ADD_BY_HASH_VALUE(hh,head,fieldname,keylen_in,hashval,add);               \
 } while(0)
 
 #define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add)                            \
@@ -207,6 +236,31 @@ do {
  HASH_FSCK(hh,head);                                                             \
 } while(0)
 
+#define HASH_ADD_KEYPTR_BY_HASH_VALUE(hh,head,keyptr,keylen_in,hashval,add)      \
+do {                                                                             \
+ unsigned _ha_bkt;                                                               \
+ (add)->hh.next = NULL;                                                          \
+ (add)->hh.key = (char*)(keyptr);                                                \
+ (add)->hh.keylen = (unsigned)(keylen_in);                                       \
+ if (!(head)) {                                                                  \
+    head = (add);                                                                \
+    (head)->hh.prev = NULL;                                                      \
+    HASH_MAKE_TABLE(hh,head);                                                    \
+ } else {                                                                        \
+    (head)->hh.tbl->tail->next = (add);                                          \
+    (add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail);         \
+    (head)->hh.tbl->tail = &((add)->hh);                                         \
+ }                                                                               \
+ (head)->hh.tbl->num_items++;                                                    \
+ (add)->hh.tbl = (head)->hh.tbl;                                                 \
+ (add)->hh.hashv = hashval;                                                      \
+ HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _ha_bkt);                     \
+ HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt],&(add)->hh);                   \
+ HASH_BLOOM_ADD((head)->hh.tbl,hashval);                                         \
+ HASH_EMIT_KEY(hh,head,keyptr,keylen_in);                                        \
+ HASH_FSCK(hh,head);                                                             \
+} while(0)
+
 #define HASH_TO_BKT( hashv, num_bkts, bkt )                                      \
 do {                                                                             \
   bkt = ((hashv) & ((num_bkts) - 1U));                                           \