Commit 224e3c14 by Mohan Maiya Committed by Commit Bot

Support optimized bitsets that need more than 64bits

For usecases that needed more than 64bits we were using std::bitset container. This has slower perf compared to the BitSetT container. Add a new class that can support large bitsets by wrapping an array of BitSet32/BitSet64 objects, depending on CPU bitness, as the container. Bug: angleproject:3877 Tests: angle_unittests.exe --gtest_filter=BitSetArrayTest* angle_white_box_perftests.exe --gtest_filter=BitSetIteratorPerfTest* Change-Id: I3f4a635f9e6974a99db7a4b592ab206aad754760 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2664733 Commit-Queue: Mohan Maiya <m.maiya@samsung.com> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarShahbaz Youssefi <syoussefi@chromium.org>
parent 54242b8f
...@@ -142,13 +142,13 @@ class BitSetT final ...@@ -142,13 +142,13 @@ class BitSetT final
ParamT first() const; ParamT first() const;
ParamT last() const; ParamT last() const;
private:
// Produces a mask of ones up to the "x"th bit. // Produces a mask of ones up to the "x"th bit.
constexpr static BitsT Mask(std::size_t x) constexpr static BitsT Mask(std::size_t x)
{ {
return ((Bit<BitsT>(static_cast<ParamT>(x - 1)) - 1) << 1) + 1; return ((Bit<BitsT>(static_cast<ParamT>(x - 1)) - 1) << 1) + 1;
} }
private:
BitsT mBits; BitsT mBits;
}; };
...@@ -546,12 +546,16 @@ struct GetBitSet<N, EnableIfBitsFit<N, uint64_t>> ...@@ -546,12 +546,16 @@ struct GetBitSet<N, EnableIfBitsFit<N, uint64_t>>
{ {
using Type = BitSet64<N>; using Type = BitSet64<N>;
}; };
constexpr std::size_t kDefaultBitSetSize = 64;
using BaseBitSetType = BitSet64<kDefaultBitSetSize>;
#else #else
template <size_t N> template <size_t N>
struct GetBitSet<N, EnableIfBitsFit<N, uint32_t>> struct GetBitSet<N, EnableIfBitsFit<N, uint32_t>>
{ {
using Type = BitSet32<N>; using Type = BitSet32<N>;
}; };
constexpr std::size_t kDefaultBitSetSize = 32;
using BaseBitSetType = BitSet32<kDefaultBitSetSize>;
#endif // defined(ANGLE_IS_64_BIT_CPU) #endif // defined(ANGLE_IS_64_BIT_CPU)
} // namespace priv } // namespace priv
...@@ -559,6 +563,299 @@ struct GetBitSet<N, EnableIfBitsFit<N, uint32_t>> ...@@ -559,6 +563,299 @@ struct GetBitSet<N, EnableIfBitsFit<N, uint32_t>>
template <size_t N> template <size_t N>
using BitSet = typename priv::GetBitSet<N>::Type; using BitSet = typename priv::GetBitSet<N>::Type;
template <std::size_t N>
class BitSetArray final
{
private:
static constexpr std::size_t kDefaultBitSetSizeMinusOne = priv::kDefaultBitSetSize - 1;
static constexpr std::size_t kShiftForDivision =
static_cast<std::size_t>(rx::Log2(static_cast<unsigned int>(priv::kDefaultBitSetSize)));
static constexpr std::size_t kArraySize =
((N + kDefaultBitSetSizeMinusOne) >> kShiftForDivision);
constexpr static std::size_t kLastElementCount = (N & kDefaultBitSetSizeMinusOne);
constexpr static std::size_t kLastElementMask = priv::BaseBitSetType::Mask(
kLastElementCount == 0 ? priv::kDefaultBitSetSize : kLastElementCount);
using BaseBitSet = priv::BaseBitSetType;
std::array<BaseBitSet, kArraySize> mBaseBitSetArray;
public:
BitSetArray();
BitSetArray(const BitSetArray<N> &other);
class Reference final
{
public:
~Reference() {}
Reference &operator=(bool x)
{
mParent.set(mPosition, x);
return *this;
}
explicit operator bool() const { return mParent.test(mPosition); }
private:
friend class BitSetArray;
Reference(BitSetArray &parent, std::size_t pos) : mParent(parent), mPosition(pos) {}
BitSetArray &mParent;
std::size_t mPosition;
};
class Iterator final
{
public:
Iterator(const BitSetArray<N> &bitSetArray, std::size_t index);
Iterator &operator++();
bool operator==(const Iterator &other) const;
bool operator!=(const Iterator &other) const;
size_t operator*() const;
private:
const BitSetArray &mParent;
size_t mIndex;
typename BaseBitSet::Iterator mCurrentIterator;
};
constexpr std::size_t size() const { return N; }
Iterator begin() const { return Iterator(*this, 0); }
Iterator end() const { return Iterator(*this, kArraySize); }
unsigned long to_ulong() const
{
// TODO(anglebug.com/5628): Handle serializing more than kDefaultBitSetSize
for (std::size_t index = 1; index < kArraySize; index++)
{
ASSERT(mBaseBitSetArray[index].none());
}
return static_cast<unsigned long>(mBaseBitSetArray[0].to_ulong());
}
BitSetArray &operator=(const BitSetArray &other);
bool operator[](std::size_t pos) const;
Reference operator[](std::size_t pos)
{
ASSERT(pos < size());
return Reference(*this, pos);
}
BitSetArray &set(std::size_t pos, bool value = true);
BitSetArray &reset();
BitSetArray &reset(std::size_t pos);
bool test(std::size_t pos) const;
bool all() const;
bool any() const;
bool none() const;
std::size_t count() const;
bool intersects(const BitSetArray &other) const;
BitSetArray<N> &flip();
};
template <std::size_t N>
BitSetArray<N>::BitSetArray()
{
static_assert(N > priv::kDefaultBitSetSize, "BitSetArray type can't support requested size.");
reset();
}
template <size_t N>
BitSetArray<N>::BitSetArray(const BitSetArray<N> &other)
{
for (std::size_t index = 0; index < kArraySize; index++)
{
mBaseBitSetArray[index] = other.mBaseBitSetArray[index];
}
}
template <size_t N>
BitSetArray<N>::Iterator::Iterator(const BitSetArray<N> &bitSetArray, std::size_t index)
: mParent(bitSetArray), mIndex(index), mCurrentIterator(mParent.mBaseBitSetArray[0].begin())
{
while (mIndex < mParent.kArraySize)
{
if (mParent.mBaseBitSetArray[mIndex].any())
{
break;
}
mIndex++;
}
if (mIndex < mParent.kArraySize)
{
mCurrentIterator = mParent.mBaseBitSetArray[mIndex].begin();
}
else
{
mCurrentIterator = mParent.mBaseBitSetArray[mParent.kArraySize - 1].end();
}
}
template <std::size_t N>
typename BitSetArray<N>::Iterator &BitSetArray<N>::Iterator::operator++()
{
++mCurrentIterator;
if (mCurrentIterator == mParent.mBaseBitSetArray[mIndex].end())
{
mIndex++;
if (mIndex < mParent.kArraySize)
{
mCurrentIterator = mParent.mBaseBitSetArray[mIndex].begin();
}
}
return *this;
}
template <std::size_t N>
bool BitSetArray<N>::Iterator::operator==(const BitSetArray<N>::Iterator &other) const
{
return mCurrentIterator == other.mCurrentIterator;
}
template <std::size_t N>
bool BitSetArray<N>::Iterator::operator!=(const BitSetArray<N>::Iterator &other) const
{
return mCurrentIterator != other.mCurrentIterator;
}
template <std::size_t N>
std::size_t BitSetArray<N>::Iterator::operator*() const
{
return (mIndex * priv::kDefaultBitSetSize) + *mCurrentIterator;
}
template <std::size_t N>
BitSetArray<N> &BitSetArray<N>::operator=(const BitSetArray<N> &other)
{
for (std::size_t index = 0; index < kArraySize; index++)
{
mBaseBitSetArray[index] = other.mBaseBitSetArray[index];
}
return *this;
}
template <std::size_t N>
bool BitSetArray<N>::operator[](std::size_t pos) const
{
ASSERT(pos < size());
return test(pos);
}
template <std::size_t N>
BitSetArray<N> &BitSetArray<N>::set(std::size_t pos, bool value)
{
ASSERT(pos < size());
// Get the index and offset, then set the bit
size_t index = pos >> kShiftForDivision;
size_t offset = pos & kDefaultBitSetSizeMinusOne;
mBaseBitSetArray[index].set(offset, value);
return *this;
}
template <std::size_t N>
BitSetArray<N> &BitSetArray<N>::reset()
{
for (BaseBitSet &baseBitSet : mBaseBitSetArray)
{
baseBitSet.reset();
}
return *this;
}
template <std::size_t N>
BitSetArray<N> &BitSetArray<N>::reset(std::size_t pos)
{
ASSERT(pos < size());
return set(pos, false);
}
template <std::size_t N>
bool BitSetArray<N>::test(std::size_t pos) const
{
ASSERT(pos < size());
// Get the index and offset, then test the bit
size_t index = pos >> kShiftForDivision;
size_t offset = pos & kDefaultBitSetSizeMinusOne;
return mBaseBitSetArray[index].test(offset);
}
template <std::size_t N>
bool BitSetArray<N>::all() const
{
static priv::BaseBitSetType kLastElementBitSet = priv::BaseBitSetType(kLastElementMask);
for (std::size_t index = 0; index < kArraySize - 1; index++)
{
if (!mBaseBitSetArray[index].all())
{
return false;
}
}
// The last element in mBaseBitSetArray may need special handling
return mBaseBitSetArray[kArraySize - 1] == kLastElementBitSet;
}
template <std::size_t N>
bool BitSetArray<N>::any() const
{
for (const BaseBitSet &baseBitSet : mBaseBitSetArray)
{
if (baseBitSet.any())
{
return true;
}
}
return false;
}
template <std::size_t N>
bool BitSetArray<N>::none() const
{
for (const BaseBitSet &baseBitSet : mBaseBitSetArray)
{
if (!baseBitSet.none())
{
return false;
}
}
return true;
}
template <std::size_t N>
std::size_t BitSetArray<N>::count() const
{
size_t count = 0;
for (const BaseBitSet &baseBitSet : mBaseBitSetArray)
{
count += baseBitSet.count();
}
return count;
}
template <std::size_t N>
bool BitSetArray<N>::intersects(const BitSetArray<N> &other) const
{
for (std::size_t index = 0; index < kArraySize; index++)
{
if (mBaseBitSetArray[index].bits() & other.mBaseBitSetArray[index].bits())
{
return true;
}
}
return false;
}
template <std::size_t N>
BitSetArray<N> &BitSetArray<N>::flip()
{
for (BaseBitSet &baseBitSet : mBaseBitSetArray)
{
baseBitSet.flip();
}
// The last element in mBaseBitSetArray may need special handling
mBaseBitSetArray[kArraySize - 1] &= kLastElementMask;
return *this;
}
} // namespace angle } // namespace angle
template <size_t N, typename BitsT, typename ParamT> template <size_t N, typename BitsT, typename ParamT>
......
...@@ -346,4 +346,92 @@ TYPED_TEST(BitSetIteratorTest, ResetLaterBit) ...@@ -346,4 +346,92 @@ TYPED_TEST(BitSetIteratorTest, ResetLaterBit)
EXPECT_EQ(expectedValues, actualValues); EXPECT_EQ(expectedValues, actualValues);
} }
template <typename T>
class BitSetArrayTest : public testing::Test
{
protected:
T mBitSet;
};
using BitSetArrayTypes =
::testing::Types<BitSetArray<65>, BitSetArray<128>, BitSetArray<130>, BitSetArray<511>>;
TYPED_TEST_SUITE(BitSetArrayTest, BitSetArrayTypes);
TYPED_TEST(BitSetArrayTest, BasicTest)
{
TypeParam &mBits = this->mBitSet;
EXPECT_FALSE(mBits.all());
EXPECT_FALSE(mBits.any());
EXPECT_TRUE(mBits.none());
EXPECT_EQ(mBits.count(), 0u);
// Verify set on a single bit
mBits.set(45);
for (auto bit : mBits)
{
EXPECT_EQ(bit, 45u);
}
mBits.reset(45);
// Set every bit to 1.
for (size_t i = 0; i < mBits.size(); ++i)
{
mBits.set(i);
EXPECT_EQ(mBits.all(), i + 1 == mBits.size());
EXPECT_TRUE(mBits.any());
EXPECT_FALSE(mBits.none());
EXPECT_EQ(mBits.count(), i + 1);
}
// Reset odd bits to 0.
for (size_t i = 1; i < mBits.size(); i += 2)
{
mBits.reset(i);
EXPECT_FALSE(mBits.all());
EXPECT_TRUE(mBits.any());
EXPECT_FALSE(mBits.none());
EXPECT_EQ(mBits.count(), mBits.size() - i / 2 - 1);
}
// Make sure the bit pattern is what we expect at this point.
// All even bits should be set
for (size_t i = 0; i < mBits.size(); ++i)
{
EXPECT_EQ(mBits.test(i), i % 2 == 0);
EXPECT_EQ(static_cast<bool>(mBits[i]), i % 2 == 0);
}
// Reset everything.
mBits.reset();
EXPECT_FALSE(mBits.all());
EXPECT_FALSE(mBits.any());
EXPECT_TRUE(mBits.none());
EXPECT_EQ(mBits.count(), 0u);
// Test intersection logic
TypeParam testBitSet;
testBitSet.set(1);
testBitSet.set(3);
testBitSet.set(5);
EXPECT_FALSE(mBits.intersects(testBitSet));
mBits.set(3);
EXPECT_TRUE(mBits.intersects(testBitSet));
mBits.set(4);
EXPECT_TRUE(mBits.intersects(testBitSet));
mBits.reset(3);
EXPECT_FALSE(mBits.intersects(testBitSet));
testBitSet.set(4);
EXPECT_TRUE(mBits.intersects(testBitSet));
// Test that flip works.
// Reset everything.
mBits.reset();
EXPECT_EQ(mBits.count(), 0u);
mBits.flip();
EXPECT_EQ(mBits.count(), mBits.size());
}
} // anonymous namespace } // anonymous namespace
...@@ -52,7 +52,9 @@ using TestTypes = Types<angle::IterableBitSet<32>, ...@@ -52,7 +52,9 @@ using TestTypes = Types<angle::IterableBitSet<32>,
angle::BitSet32<32>, angle::BitSet32<32>,
angle::BitSet64<32>, angle::BitSet64<32>,
angle::IterableBitSet<64>, angle::IterableBitSet<64>,
angle::BitSet64<64>>; angle::BitSet64<64>,
angle::BitSet<96>,
angle::BitSetArray<96>>;
TYPED_TEST_SUITE(BitSetIteratorPerfTest, TestTypes); TYPED_TEST_SUITE(BitSetIteratorPerfTest, TestTypes);
TYPED_TEST(BitSetIteratorPerfTest, Run) TYPED_TEST(BitSetIteratorPerfTest, Run)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment