]> granicus.if.org Git - postgresql/commitdiff
Speed up AllocSetFreeIndex, which is a significant cost in palloc and pfree,
authorTom Lane <tgl@sss.pgh.pa.us>
Tue, 21 Jul 2009 19:53:12 +0000 (19:53 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Tue, 21 Jul 2009 19:53:12 +0000 (19:53 +0000)
by using a lookup table instead of a naive shift-and-count loop.  Based on
code originally posted by Sean Eron Anderson at
http://graphics.stanford.edu/%7eseander/bithacks.html.
Greg Stark did the research and benchmarking to show that this is what
we should use.  Jeremy Kerr first noticed that this is a hotspot that
could be optimized, though we ended up not using his suggestion of
platform-specific bit-searching code.

src/backend/utils/mmgr/aset.c

index f11e15d57c597f87c427fba0086ef1769c46802f..520c9acbcceaba16056ae05f2f22bc946bfa96e5 100644 (file)
@@ -11,7 +11,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/utils/mmgr/aset.c,v 1.79 2009/06/11 14:49:06 momjian Exp $
+ *       $PostgreSQL: pgsql/src/backend/utils/mmgr/aset.c,v 1.80 2009/07/21 19:53:12 tgl Exp $
  *
  * NOTE:
  *     This is a new (Feb. 05, 1999) implementation of the allocation set
@@ -238,6 +238,17 @@ static MemoryContextMethods AllocSetMethods = {
 #endif
 };
 
+/*
+ * Table for AllocSetFreeIndex
+ */
+#define LT16(n) n, n, n, n, n, n, n, n, n, n, n, n, n, n, n, n
+
+static const unsigned char LogTable256[256] =
+{
+       0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4,
+       LT16(5), LT16(6), LT16(6), LT16(7), LT16(7), LT16(7), LT16(7),
+       LT16(8), LT16(8), LT16(8), LT16(8), LT16(8), LT16(8), LT16(8), LT16(8)
+};
 
 /* ----------
  * Debug macros
@@ -266,18 +277,30 @@ static MemoryContextMethods AllocSetMethods = {
 static inline int
 AllocSetFreeIndex(Size size)
 {
-       int                     idx = 0;
+       int                     idx;
+       unsigned int t,
+                               tsize;
 
-       if (size > 0)
+       if (size > (1 << ALLOC_MINBITS))
        {
-               size = (size - 1) >> ALLOC_MINBITS;
-               while (size != 0)
-               {
-                       idx++;
-                       size >>= 1;
-               }
+               tsize = (size - 1) >> ALLOC_MINBITS;
+
+               /*
+                * At this point we need to obtain log2(tsize)+1, ie, the number
+                * of not-all-zero bits at the right.  We used to do this with a
+                * shift-and-count loop, but this function is enough of a hotspot
+                * to justify micro-optimization effort.  The best approach seems
+                * to be to use a lookup table.  Note that this code assumes that
+                * ALLOCSET_NUM_FREELISTS <= 17, since we only cope with two bytes
+                * of the tsize value.
+                */
+               t = tsize >> 8;
+               idx = t ? LogTable256[t] + 8 : LogTable256[tsize];
+
                Assert(idx < ALLOCSET_NUM_FREELISTS);
        }
+       else
+               idx = 0;
 
        return idx;
 }