prevPowTwo / faster bit operations
authorBen Maurer <bmaurer@fb.com>
Tue, 25 Oct 2016 03:14:08 +0000 (20:14 -0700)
committerFacebook Github Bot <facebook-github-bot-bot@fb.com>
Tue, 25 Oct 2016 03:23:29 +0000 (20:23 -0700)
Summary:
Add a prevPowTwo method to bits.h and optimize the current code for
GCCs output.

Reviewed By: ot

Differential Revision: D4072341

fbshipit-source-id: 6e949d0bfcf88ff8500022939d08a2b5aa9e00c9

folly/Bits.h
folly/test/BitsTest.cpp

index 43ceb40a32376f150dd8162c03657264a9bfdc52..9742a06665fb6db6ef4394ccfb1f451b6bab76c9 100644 (file)
@@ -143,7 +143,9 @@ typename std::enable_if<
    sizeof(T) <= sizeof(unsigned int)),
   unsigned int>::type
   findLastSet(T x) {
-  return x ? 8 * sizeof(unsigned int) - __builtin_clz(x) : 0;
+  // If X is a power of two X - Y = ((X - 1) ^ Y) + 1. Doing this transformation
+  // allows GCC to remove its own xor that it adds to implement clz using bsr
+  return x ? ((8 * sizeof(unsigned int) - 1) ^ __builtin_clz(x)) + 1 : 0;
 }
 
 template <class T>
@@ -155,7 +157,7 @@ typename std::enable_if<
    sizeof(T) <= sizeof(unsigned long)),
   unsigned int>::type
   findLastSet(T x) {
-  return x ? 8 * sizeof(unsigned long) - __builtin_clzl(x) : 0;
+  return x ? ((8 * sizeof(unsigned long) - 1) ^ __builtin_clzl(x)) + 1 : 0;
 }
 
 template <class T>
@@ -167,7 +169,8 @@ typename std::enable_if<
    sizeof(T) <= sizeof(unsigned long long)),
   unsigned int>::type
   findLastSet(T x) {
-  return x ? 8 * sizeof(unsigned long long) - __builtin_clzll(x) : 0;
+  return x ? ((8 * sizeof(unsigned long long) - 1) ^ __builtin_clzll(x)) + 1
+           : 0;
 }
 
 template <class T>
@@ -190,10 +193,16 @@ nextPowTwo(T v) {
 }
 
 template <class T>
-inline constexpr
-typename std::enable_if<
-  std::is_integral<T>::value && std::is_unsigned<T>::value,
-  bool>::type
+inline FOLLY_INTRINSIC_CONSTEXPR typename std::
+    enable_if<std::is_integral<T>::value && std::is_unsigned<T>::value, T>::type
+    prevPowTwo(T v) {
+  return v ? (T(1) << (findLastSet(v) - 1)) : 0;
+}
+
+template <class T>
+inline constexpr typename std::enable_if<
+    std::is_integral<T>::value && std::is_unsigned<T>::value,
+    bool>::type
 isPowTwo(T v) {
   return (v != 0) && !(v & (v - 1));
 }
index 4f6ed6764e5a8ae6f6086a3c2b2407329467e8ab..96abd60c8e0ffe221610165a4ba74ffb35eb19a7 100644 (file)
@@ -114,6 +114,29 @@ TEST(Bits, nextPowTwoClz) {
   EXPECT_EQ(1ull << 63, nextPowTwo((1ull << 62) + 1));
 }
 
+TEST(Bits, prevPowTwoClz) {
+  EXPECT_EQ(0, prevPowTwo(0u));
+  EXPECT_EQ(1, prevPowTwo(1u));
+  EXPECT_EQ(2, prevPowTwo(2u));
+  EXPECT_EQ(2, prevPowTwo(3u));
+  EXPECT_EQ(4, prevPowTwo(4u));
+  EXPECT_EQ(4, prevPowTwo(5u));
+  EXPECT_EQ(4, prevPowTwo(6u));
+  EXPECT_EQ(4, prevPowTwo(7u));
+  EXPECT_EQ(8, prevPowTwo(8u));
+  EXPECT_EQ(8, prevPowTwo(9u));
+  EXPECT_EQ(8, prevPowTwo(13u));
+  EXPECT_EQ(16, prevPowTwo(16u));
+  EXPECT_EQ(256, prevPowTwo(510u));
+  EXPECT_EQ(256, prevPowTwo(511u));
+  EXPECT_EQ(512, prevPowTwo(512u));
+  EXPECT_EQ(512, prevPowTwo(513u));
+  EXPECT_EQ(512, prevPowTwo(777u));
+  EXPECT_EQ(1ul << 30, prevPowTwo((1ul << 31) - 1));
+  EXPECT_EQ(1ull << 31, prevPowTwo((1ull << 32) - 1));
+  EXPECT_EQ(1ull << 62, prevPowTwo((1ull << 62) + 1));
+}
+
 TEST(Bits, isPowTwo) {
   EXPECT_FALSE(isPowTwo(0u));
   EXPECT_TRUE(isPowTwo(1ul));