diff --git a/include/bx/bitarray.h b/include/bx/bitarray.h index fda3334..a847d64 100644 --- a/include/bx/bitarray.h +++ b/include/bx/bitarray.h @@ -14,7 +14,7 @@ namespace bx { class BitArrayViewBase; - /// Walks set bits of a mask, lowest first, by shifting the mask down to the next set bit. + /// Walks set bits of a mask, lowest first, by clearing the lowest set bit. /// /// @tparam MaskT Unsigned integer type of the mask. /// @@ -37,8 +37,7 @@ namespace bx MaskT getMask() const; private: - MaskT m_mask; - uint8_t m_idx; + MaskT m_mask; }; /// Range-for iterator over set bits of a bit array, in index order. Dereferences to the bit diff --git a/include/bx/inline/bitarray.inl b/include/bx/inline/bitarray.inl index b102d79..6756da9 100644 --- a/include/bx/inline/bitarray.inl +++ b/include/bx/inline/bitarray.inl @@ -65,23 +65,14 @@ namespace bx template inline BitMaskToIndexIteratorT::BitMaskToIndexIteratorT(MaskT _mask) - : m_mask(0) - , m_idx(0) + : m_mask(_mask) { - const uint8_t ntz = countTrailingZeros(_mask); - m_mask = 0 == _mask ? MaskT(0) : MaskT(_mask >> ntz); - m_idx = ntz; } template inline void BitMaskToIndexIteratorT::next() { - m_mask = MaskT(m_mask >> 1); - m_idx += 1; - - const uint8_t ntz = countTrailingZeros(m_mask); - m_mask = 0 == m_mask ? MaskT(0) : MaskT(m_mask >> ntz); - m_idx += ntz; + m_mask = MaskT(m_mask & (m_mask - 1) ); } template @@ -93,13 +84,13 @@ namespace bx template inline uint8_t BitMaskToIndexIteratorT::getIndex() const { - return m_idx; + return countTrailingZeros(m_mask); } template inline MaskT BitMaskToIndexIteratorT::getMask() const { - return m_mask; + return MaskT(m_mask >> getIndex() ); } inline BitArrayIterator::BitArrayIterator(const BitArrayViewBase& _view, uint32_t _idx) diff --git a/tests/bitarray_test.cpp b/tests/bitarray_test.cpp index 93acb9a..a8cbe18 100644 --- a/tests/bitarray_test.cpp +++ b/tests/bitarray_test.cpp @@ -1278,6 +1278,107 @@ TEST_CASE("BitArray-ops-bench", "[.][!benchmark]") } } +template +static uint32_t sumMaskIndices(const MaskT* _masks, uint32_t _numMasks, const uint32_t* _table) +{ + uint32_t sum = 0; + + for (uint32_t ii = 0; ii < _numMasks; ++ii) + { + for (bx::BitMaskToIndexIteratorT it(_masks[ii]); !it.isDone(); it.next() ) + { + sum += _table[it.getIndex()]; + } + } + + return sum; +} + +static uint32_t sumSetBits(const bx::BitArrayViewBase& _bs) +{ + uint32_t sum = 0; + + for (const uint32_t idx : _bs) + { + sum += idx; + } + + return sum; +} + +TEST_CASE("BitArray-iterate-bench", "[.][!benchmark]") +{ + constexpr uint32_t kNumMasks = 4096; + constexpr uint32_t kNumBits = 64<<10; + + bx::RngMwc rng; + + uint32_t table[64]; + + for (uint32_t ii = 0; ii < BX_COUNTOF(table); ++ii) + { + table[ii] = rng.gen(); + } + + static uint32_t streamMasks[kNumMasks]; + static uint32_t bindingMasks[kNumMasks]; + static uint64_t randomMasks[kNumMasks]; + + for (uint32_t ii = 0; ii < kNumMasks; ++ii) + { + streamMasks[ii] = rng.gen() & 0xf; + bindingMasks[ii] = rng.gen() & rng.gen() & 0xffff; + randomMasks[ii] = (uint64_t(rng.gen() ) << 32) | rng.gen(); + } + + BENCHMARK("mask streams 4 bits 4K") + { + return sumMaskIndices(streamMasks, kNumMasks, table); + }; + + BENCHMARK("mask bindings 16 bits 4K") + { + return sumMaskIndices(bindingMasks, kNumMasks, table); + }; + + BENCHMARK("mask random 64 bits 4K") + { + return sumMaskIndices(randomMasks, kNumMasks, table); + }; + + alignas(64) static uint64_t bits[kNumBits/64]; + bx::MutableBitArrayView bs(bits, sizeof(bits) ); + + bs.set(0, kNumBits, true); + + BENCHMARK("iterate dense 64K") + { + return sumSetBits(bs); + }; + + for (uint32_t ii = 0; ii < kNumBits/64; ++ii) + { + bits[ii] = (uint64_t(rng.gen() ) << 32) | rng.gen(); + } + + BENCHMARK("iterate random 64K") + { + return sumSetBits(bs); + }; + + bs.clear(); + + for (uint32_t ii = 0; ii < kNumBits; ii += 64) + { + bs.set(ii + rng.gen() % 64, true); + } + + BENCHMARK("iterate 1/64 64K") + { + return sumSetBits(bs); + }; +} + static void fillRandom(bx::RngMwc& _rng, uint64_t* _bits, uint32_t _numWords) { const uint32_t mode = _rng.gen() % 4;