]> Savannah Git Hosting - gnulib.git/commitdiff
bitset: use ffsl to accelerate iterations over set bits
authorAkim Demaille <akim.demaille@gmail.com>
Sat, 14 Nov 2020 15:58:23 +0000 (16:58 +0100)
committerAkim Demaille <akim.demaille@gmail.com>
Sun, 15 Nov 2020 17:00:21 +0000 (18:00 +0100)
Currently we iterate over words bit by bit.  Instead, we should jump
from set bit to set bit.
Suggested by Bruno Haible.

* modules/bitset: Depend upon ffsl.
* lib/bitset/base.h (bitset_ffs, BITSET_FOR_EACH_BIT): New.
* lib/bitset/array.c (abitset_list): Use BITSET_FOR_EACH_BIT.

ChangeLog
lib/bitset/array.c
lib/bitset/base.h
modules/bitset

index 1eb7396d76b8479f25f014067b5577691c284a33..70e2f9dd2b56e209c68ba8a411fab708b506a9db 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2020-11-15  Akim Demaille  <akim@lrde.epita.fr>
+
+       bitset: use ffsl to accelerate iterations over set bits
+       Suggested by Bruno Haible.
+       * modules/bitset: Depend upon ffsl.
+       * lib/bitset/base.h (bitset_ffs, BITSET_FOR_EACH_BIT): New.
+       * lib/bitset/array.c (abitset_list): Use BITSET_FOR_EACH_BIT.
+
 2020-11-15  Akim Demaille  <akim@lrde.epita.fr>
 
        bitset: more tests
index 1db583891433431f36271ac3577f2ecc9f81ebaa..6c5f7ed34f936d7513daebddbc47e9ed62116cc2 100644 (file)
@@ -227,18 +227,15 @@ abitset_list (bitset src, bitset_bindex *list,
 
           bitoff = windex * BITSET_WORD_BITS;
           bitset_word word = srcp[windex] >> bitno;
-          for (bitno = bitoff + bitno; word; bitno++)
+          bitno = bitoff + bitno;
+          BITSET_FOR_EACH_BIT (pos, word)
             {
-              if (word & 1)
+              list[count++] = bitno + pos;
+              if (count >= num)
                 {
-                  list[count++] = bitno;
-                  if (count >= num)
-                    {
-                      *next = bitno + 1;
-                      return count;
-                    }
+                  *next = bitno + pos + 1;
+                  return count;
                 }
-              word >>= 1;
             }
           windex++;
         }
@@ -251,31 +248,20 @@ abitset_list (bitset src, bitset_bindex *list,
       if (!word)
         continue;
 
+      /* Is there enough room to avoid checking in each iteration? */
       if ((count + BITSET_WORD_BITS) < num)
-        {
-          for (bitno = bitoff; word; bitno++)
-            {
-              if (word & 1)
-                list[count++] = bitno;
-              word >>= 1;
-            }
-        }
+        BITSET_FOR_EACH_BIT (pos, word)
+          list[count++] = bitoff + pos;
       else
-        {
-          for (bitno = bitoff; word; bitno++)
-            {
-              if (word & 1)
-                {
-                  list[count++] = bitno;
-                  if (count >= num)
-                    {
-                      *next = bitno + 1;
-                      return count;
-                    }
-                }
-              word >>= 1;
-            }
-        }
+        BITSET_FOR_EACH_BIT (pos, word)
+          {
+            list[count++] = bitoff + pos;
+            if (count >= num)
+              {
+                *next = bitoff + pos + 1;
+                return count;
+              }
+          }
     }
 
   *next = bitoff;
index 2ae7b20802ad46e8cf853a1af2368ecdebdee18f..50569a0fc16858efee9bfcbcfbce2d30eca51713 100644 (file)
@@ -24,6 +24,7 @@
 #include <limits.h>
 #include <stdbool.h>
 #include <stddef.h>
+#include <string.h> /* ffsl */
 
 #include "attribute.h"
 #include "xalloc.h"
@@ -52,6 +53,14 @@ extern const char * const bitset_type_names[];
 typedef unsigned long bitset_word;
 #define BITSET_WORD_BITS ((unsigned) (CHAR_BIT * sizeof (bitset_word)))
 
+/* Iterate over each set bit of WORD.
+   Each iteration sets POS to the 0-based index of the next set bit in WORD.
+   Repeatedly resets bits in WORD in place until it's null.  */
+#define BITSET_FOR_EACH_BIT(Pos, Word)                  \
+  for (int Pos = bitset_ffs (Word);                     \
+       0 <= Pos;                                        \
+       Word ^= 1UL << Pos, Pos = bitset_ffs (Word))
+
 /* Bit index.  In theory we might need a type wider than size_t, but
    in practice we lose at most a factor of CHAR_BIT by going with
    size_t, and that is good enough.  If this type is changed to be
@@ -60,6 +69,14 @@ typedef unsigned long bitset_word;
    The bit and word index types must be unsigned.  */
 typedef size_t bitset_bindex;
 
+/* First first set bit in WORD.
+   Indexes start at 0, return -1 if WORD is null. */
+static inline
+int bitset_ffs (bitset_word word)
+{
+  return ffsl ((long) word) - 1;
+}
+
 /* Word index.  */
 typedef size_t bitset_windex;
 
index 21cde24ac7c2c5debd60a31cd88d1d2db7a3b87f..2516c2861b745dc35097ff85919cf30d230aea0e 100644 (file)
@@ -19,6 +19,7 @@ lib/bitset/vector.h
 Depends-on:
 attribute
 c99
+ffsl
 fopen-gnu
 gettext-h
 obstack