[Quipu-dev] quipu/quipu: Improve table implementation

Back to archive index

scmno****@osdn***** scmno****@osdn*****
Thu Jun 7 05:27:26 JST 2018


changeset 5a8261f71055 in quipu/quipu
details: http://hg.osdn.jp/view/quipu/quipu?cmd=changeset;node=5a8261f71055
user: Agustina Arzille <avarz****@riseu*****>
date: Wed Jun 06 17:27:16 2018 -0300
description: Improve table implementation

diffstat:

 table.cpp |  145 ++++++++++++++++++++++++++-----------------------------------
 table.h   |    1 -
 2 files changed, 61 insertions(+), 85 deletions(-)

diffs (truncated from 329 to 300 lines):

diff -r 41f759fb7070 -r 5a8261f71055 table.cpp
--- a/table.cpp	Mon Jun 04 16:34:44 2018 -0300
+++ b/table.cpp	Wed Jun 06 17:27:16 2018 -0300
@@ -28,7 +28,7 @@
 }
 
 static inline object&
-tabvec_pad (array *vecp)
+tabvec_pidx (array *vecp)
 {
   return (vecp->data[3]);
 }
@@ -48,9 +48,6 @@
     "table entries must be correctly aligned for double atomic CAS");
 #endif
 
-static const uint32_t SECONDARY_KEYS[] = { 3, 5, 7, 11, 13, 17, 19, 23 };
-static const int N_SECONDARY_KEYS = QP_NELEM (SECONDARY_KEYS);
-
 static inline bool
 table_equal (interpreter *interp, const table *tp,
   object k1, object k2)
@@ -75,37 +72,34 @@
   return ((uint32_t)as_int (interp->retval));
 }
 
-static uint32_t
-table_size (uint32_t isz)
+static const uint32_t SECONDARY_KEYS[] = { 2, 3, 5, 7 };
+static const int N_SECONDARY_KEYS = QP_NELEM (SECONDARY_KEYS);
+
+static const uint32_t PRIMES[] =
 {
-  if (isz <= SECONDARY_KEYS[N_SECONDARY_KEYS - 1])
-    return (SECONDARY_KEYS[N_SECONDARY_KEYS - 1]);
-  else
-    for (isz |= 1 ; ; isz += 2)
-      { /* Return a size that is prime to all secondary
-         * keys (relatively speaking, at least). */
-        for (int i = 0; i < N_SECONDARY_KEYS; ++i)
-          if (isz % SECONDARY_KEYS[i] == 0)
-            goto next;
-            
-         return (isz);
-       next: ;
-      }
-}
+  0xb, 0x25, 0x71, 0x15b, 0x419, 0xc4d, 0x24f5, 0x6ee3, 0x14cb3, 0x3e61d,
+  0xbb259, 0x23170f, 0x694531, 0x13bcf95, 0x3b36ec3, 0xb1a4c4b, 0x214ee4e3,
+  0x63ecaead
+};
 
 static int
-compute_hsize (uint32_t size, float mv_size,
-  float mv_ratio, int *vsizep)
+compute_hsize (uint32_t min_size, float mv_ratio, int *idxp)
 {
-  uint32_t tmp = (uint32_t)(mv_size * size + 0.5f);
-  uint32_t nsize = max (30u, tmp);
-    
-  if (nsize < size)
-    nsize = size + 1;   // Hopefully this doesn't happen.
-    
-  *vsizep = (int)table_size (max (nsize + 2,
-    (uint32_t)(0.5f + mv_ratio * nsize)));
-  return ((int)nsize);
+  int i1 = 0, i2 = QP_NELEM (PRIMES), cnt = i2 - i1;
+  while (cnt > 0)
+    {
+      int step = cnt >> 1;
+      if (PRIMES[step] < min_size)
+        {
+          i1 = step;
+          cnt -= step + 1;
+        }
+      else
+        cnt = step;
+    }
+
+  *idxp = i1;
+  return ((int)(PRIMES[i1] * mv_ratio));
 }
 
 // Special values used by the table implementation.
@@ -117,9 +111,9 @@
   "FREE_HASH and DELETED_KEY must not be equal");
 
 static array*
-make_tabvec (interpreter *interp, uint32_t size)
+make_tabvec (interpreter *interp, int prime_idx)
 {
-  uint32_t tsize = tabvec_idx (size);
+  uint32_t size = PRIMES[prime_idx], tsize = tabvec_idx (size);
 #ifdef QP_HAS_ATOMIC_CASX
   array *ret = as_array (alloc_array (interp, tsize + 1, FREE_HASH));
 
@@ -132,10 +126,10 @@
   array *ret = as_array (alloc_array (interp, tsize, FREE_HASH));
 #endif
 
-  tabvec_tab(ret) = intobj (0);   // Will be set later.
+  tabvec_tab(ret) = intobj (0);
   tabvec_size(ret) = intobj (size);
-  tabvec_cnt(ret)  = intobj (0);
-  tabvec_pad(ret) = intobj (0);   // Not used for now.
+  tabvec_cnt(ret) = intobj (0);
+  tabvec_pidx(ret) = intobj (prime_idx);
 
   return (ret);
 }
@@ -145,16 +139,16 @@
   object tst, object hashfn)
 {
   evh_guard eg (interp);
-  int tsize, gt = compute_hsize (size ? size - 1 : 1, 1.0f, 0.85f, &tsize);
+  float mv_ratio = 0.85f;
+  int pidx, gt = compute_hsize (0, mv_ratio, &pidx);
   table *ret = (table *)alloch (sizeof (*ret), typecode::TABLE);
-  array *vec = make_tabvec (interp, tsize);
+  array *vec = make_tabvec (interp, pidx);
 
   lwlock_init (&ret->lock);
-  ret->mv_size = 1.5f;
   ret->cmpfct = tst == NIL ? UNBOUND : tst;
   ret->hashfct = hashfn == NIL ? UNBOUND : hashfn;
   ret->grow_limit = gt;
-  ret->mv_ratio = 0.85f;
+  ret->mv_ratio = mv_ratio;
   ret->vector = interp->alval;
   interp->alval = tabvec_tab (vec) = ret->as_obj ();
   gcregister (interp, ret);
@@ -235,7 +229,7 @@
   array *vecp, object key)
 {
   /* Same as above, only this function is called when migrating, which means
-   * it'll never return failure, and it can also reuse deleted entries. */
+   * it cannot return failure. */
   uint32_t hashcode = table_hash (interp, tp, key);
   int entries = as_int (tabvec_size (vecp));
   int idx = (int)(hashcode % entries);
@@ -263,12 +257,9 @@
 table_migrate_lk (interpreter *interp, table *tp)
 {
   array *oldvp = as_array (tp->vector);
-  int i, cnt = as_int (tabvec_size (oldvp));
-  int vsize, gt = compute_hsize (cnt * 2,
-    tp->mv_size, tp->mv_ratio, &vsize);
-  array *newvp = make_tabvec (interp, vsize);
+  array *newvp = make_tabvec (interp, as_int (tabvec_pidx (oldvp)) + 1);
 
-  for (i = tabvec_idx (0); i < oldvp->len; i += 2)
+  for (int i = tabvec_idx (0); i < oldvp->len; i += 2)
     {
       int new_idx;
 
@@ -280,7 +271,9 @@
       newvp->data[new_idx + 1] = oldvp->data[i + 1];
     }
 
-  tp->grow_limit = gt - as_int (tabvec_cnt (oldvp));
+  tp->grow_limit = (atomic_t)(tp->mv_ratio *
+    as_int (tabvec_size (newvp)) - as_int (tabvec_cnt (oldvp)));
+
   tabvec_cnt(newvp) = tabvec_cnt (oldvp);
   tabvec_tab(newvp) = tp->as_obj ();
   tp->vector = newvp->as_obj ();
@@ -320,27 +313,23 @@
   if (tp->grow_limit <= 0)
     {
       array *oldvp = as_array (tp->vector);
-      int cnt = as_int (tabvec_size (oldvp));
-      int vsize, gt = compute_hsize (cnt * 2,
-        tp->mv_size, tp->mv_ratio, &vsize);
-      array *newvp = make_tabvec (interp, vsize);
+      array *newvp = make_tabvec (interp, as_int (tabvec_pidx (oldvp)) + 1);
       int nelem = 0;
-      valref tmp (interp, intobj (0));
 
       g.set_vec (oldvp);
       
       for (int i = tabvec_idx (0); i < oldvp->len; i += 2)
         {
           object key = oldvp->data[i];
-          *tmp = atomic_or ((atomic_t *)&oldvp->data[i + 1], EXTRA_BIT);
+          object val = atomic_or ((atomic_t *)&oldvp->data[i + 1], EXTRA_BIT);
 
           /* No other thread can be migrating the table at this point,
            * so it's safe to do a simplified test here. */
-          if (valid_key_p (key) && *tmp != DELETED_VAL && *tmp != FREE_HASH)
+          if (valid_key_p (key) && val != DELETED_VAL && val != FREE_HASH)
             {
               int new_idx = growtab_probe (interp, tp, newvp, key);
               newvp->data[new_idx + 0] = key;
-              newvp->data[new_idx + 1] = *tmp;
+              newvp->data[new_idx + 1] = val;
               ++nelem;
             }
         }
@@ -350,7 +339,9 @@
       // Set up the new table.
       tabvec_cnt(newvp) = intobj (nelem);
       tabvec_tab(newvp) = tp->as_obj ();
-      tp->grow_limit = gt - nelem;
+      tp->grow_limit = (atomic_t)(tp->mv_ratio *
+        as_int (tabvec_size (newvp))) - nelem;
+      atomic_mfence_rel ();
       /* At this point, another thread may decrement the growth limit from
        * the wrong table vector. That's fine, it just means we'll have to
        * migrate sooner than necessary. */
@@ -368,15 +359,12 @@
       array *vecp = as_array (interp->aux);
       int idx = table_probe (interp, tp, key, false);
 
-      if (qp_unlikely (vecp != as_array (tp->vector)))
-        continue;
-      else if (idx < 0)
+      if (idx < 0)
         return (dfl);
-      else
+      else if (qp_likely (vecp == as_array (tp->vector)))
         {
-          interp->aux = vecp->data[idx + 1] & ~EXTRA_BIT;
-          return (interp->aux == DELETED_VAL ||
-            interp->aux == FREE_HASH ? dfl : interp->aux);
+          object ret = vecp->data[idx + 1] & ~EXTRA_BIT;
+          return (ret == DELETED_VAL || ret == FREE_HASH ? dfl : ret);
         }
     }
 }
@@ -480,7 +468,7 @@
 
   if (empty)
     {
-      if (tp->grow_limit <= 0)
+      if (--tp->grow_limit <= 0)
         {
           table_migrate_lk (interp, tp);
           idx = table_probe (interp, tp, key, true);
@@ -488,7 +476,6 @@
         }
 
       vecp->data[idx] = key;
-      --tp->grow_limit;
       tabvec_cnt(vecp) += intobj (1);
     }
 
@@ -521,8 +508,6 @@
               atomic_add (&tp->grow_limit, -1);
 
 #ifdef QP_ATOMIC_HAS_CASX
-              /* Make sure the key and value pairs are passed in
-               * the right order, depending on endianness. */
               if (atomic_casx (&vecp->data[idx], FREE_HASH, FREE_HASH,
 #  if QP_LITTLE_ENDIAN
                   key, val
@@ -544,10 +529,9 @@
         }
       else
         {
-          interp->aux = vecp->data[idx + 1];
-          if (interp->aux != DELETED_VAL && interp->aux != FREE_HASH &&
-              (interp->aux & EXTRA_BIT) == 0 &&
-              setv_cond (vecp, idx, interp->aux, val))
+          object tmp = vecp->data[idx + 1];
+          if (tmp != DELETED_VAL && tmp != FREE_HASH &&
+              (tmp & EXTRA_BIT) == 0 && setv_cond (vecp, idx, tmp, val))
             return (empty);
 
           continue;
@@ -572,12 +556,10 @@
 static void
 table_clr_lk (interpreter *interp, table *tp)
 {
-  int vsize, gt = compute_hsize (1,
-    tp->mv_size, tp->mv_ratio, &vsize);
-  array *vecp = make_tabvec (interp, vsize);
+  array *vecp = make_tabvec (interp, 0);
 
   tabvec_tab(vecp) = tp->as_obj ();
-  tp->grow_limit = gt;
+  tp->grow_limit = (atomic_t)(tp->mv_ratio * tabvec_size (vecp));
   tp->vector = vecp->as_obj ();
 }
 
@@ -585,8 +567,7 @@
 table_clr_mt (interpreter *interp, table *tp)
 {
   array *vecp = as_array (tp->vector);
-  int vsize, gt = compute_hsize (1, tp->mv_size, tp->mv_ratio, &vsize);
-  array *np = make_tabvec (interp, vsize);
+  array *np = make_tabvec (interp, 0);
 
   lwlock_guard g (&tp->lock);
   // Prevent any further insertions.
@@ -607,7 +588,8 @@
 #endif
 
   tabvec_tab(np) = tp->as_obj ();
-  tp->grow_limit = gt;
+  tp->grow_limit = (atomic_t)(tp->mv_ratio * tabvec_size (vecp));




More information about the Quipu-dev mailing list
Back to archive index