Use __builtin_clz() to implement log_2()
authorSimon Marlow <marlowsd@gmail.com>
Sat, 23 Apr 2016 21:14:43 +0000 (22:14 +0100)
committerSimon Marlow <marlowsd@gmail.com>
Tue, 26 Apr 2016 15:00:43 +0000 (16:00 +0100)
A microoptimisation in the block allocator.

rts/sm/BlockAlloc.c

index a633726..1c83de9 100644 (file)
@@ -199,31 +199,41 @@ initGroup(bdescr *head)
   }
 }
 
-// There are quicker non-loopy ways to do log_2, but we expect n to be
-// usually small, and MAX_FREE_LIST is also small, so the loop version
-// might well be the best choice here.
+// log base 2 (floor), needs to support up to 2^MAX_FREE_LIST
 STATIC_INLINE nat
-log_2_ceil(W_ n)
+log_2(W_ n)
 {
+#if defined(__GNUC__)
+    return __builtin_clzl(n) ^ (sizeof(StgWord)*8 - 1);
+    // generates good code on x86.  __builtin_clz() compiles to bsr+xor, but
+    // we want just bsr, so the xor here cancels out gcc's xor.
+#else
     W_ i, x;
-    x = 1;
+    x = n;
     for (i=0; i < MAX_FREE_LIST; i++) {
-        if (x >= n) return i;
-        x = x << 1;
+        x = x >> 1;
+        if (x == 0) return i;
     }
     return MAX_FREE_LIST;
+#endif
 }
 
+// log base 2 (ceiling), needs to support up to 2^MAX_FREE_LIST
 STATIC_INLINE nat
-log_2(W_ n)
+log_2_ceil(W_ n)
 {
+#if defined(__GNUC__)
+    nat r = log_2(n);
+    return (n & (n-1)) ? r+1 : r;
+#else
     W_ i, x;
-    x = n;
+    x = 1;
     for (i=0; i < MAX_FREE_LIST; i++) {
-        x = x >> 1;
-        if (x == 0) return i;
+        if (x >= n) return i;
+        x = x << 1;
     }
     return MAX_FREE_LIST;
+#endif
 }
 
 STATIC_INLINE void