Changed BitMaskToIndexIteratorT to clear lowest set bit instead of shifting mask down. (#473)

This commit is contained in:
Branimir Karadžić
2026-10-23 11:09:22 -07:00
committed by GitHub
parent f3a9adb603
commit d0ce13617d
3 changed files with 107 additions and 16 deletions
+2 -3
View File
@@ -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
+4 -13
View File
@@ -65,23 +65,14 @@ namespace bx
template<typename MaskT>
inline BitMaskToIndexIteratorT<MaskT>::BitMaskToIndexIteratorT(MaskT _mask)
: m_mask(0)
, m_idx(0)
: m_mask(_mask)
{
const uint8_t ntz = countTrailingZeros<MaskT>(_mask);
m_mask = 0 == _mask ? MaskT(0) : MaskT(_mask >> ntz);
m_idx = ntz;
}
template<typename MaskT>
inline void BitMaskToIndexIteratorT<MaskT>::next()
{
m_mask = MaskT(m_mask >> 1);
m_idx += 1;
const uint8_t ntz = countTrailingZeros<MaskT>(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<typename MaskT>
@@ -93,13 +84,13 @@ namespace bx
template<typename MaskT>
inline uint8_t BitMaskToIndexIteratorT<MaskT>::getIndex() const
{
return m_idx;
return countTrailingZeros<MaskT>(m_mask);
}
template<typename MaskT>
inline MaskT BitMaskToIndexIteratorT<MaskT>::getMask() const
{
return m_mask;
return MaskT(m_mask >> getIndex() );
}
inline BitArrayIterator::BitArrayIterator(const BitArrayViewBase& _view, uint32_t _idx)
+101
View File
@@ -1278,6 +1278,107 @@ TEST_CASE("BitArray-ops-bench", "[.][!benchmark]")
}
}
template<typename MaskT>
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<MaskT> 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;