Skip to content

Commit

Permalink
Tweaked: Bitset flip performance
Browse files Browse the repository at this point in the history
Added: More bitset unit tests
  • Loading branch information
richardbiely committed Sep 22, 2023
1 parent 35fc0be commit 0a58afb
Show file tree
Hide file tree
Showing 4 changed files with 356 additions and 100 deletions.
37 changes: 29 additions & 8 deletions include/gaia/containers/bitset.h
Original file line number Diff line number Diff line change
Expand Up @@ -235,21 +235,42 @@ namespace gaia {
//! Flips the bit at the postion \param pos
constexpr void flip(uint32_t pos) {
GAIA_ASSERT(pos < NBits);
m_data[pos / BitsPerItem] ^= ((size_type)1 << (pos % BitsPerItem));
const auto wordIdx = pos / BitsPerItem;
const auto bitIdx = pos % BitsPerItem;
m_data[wordIdx] ^= ((size_type)1 << bitIdx);
}

//! Flips all bits from \param bitFrom to \param bitTo (including)
constexpr bitset& flip(uint32_t bitFrom, uint32_t bitTo) {
GAIA_ASSERT(bitFrom <= bitTo);
GAIA_ASSERT(bitFrom < size());
GAIA_ASSERT(bitTo < size());

if GAIA_UNLIKELY (size() == 0)
return *this;
// The followign can't happen because we always have at least 1 bit
// if GAIA_UNLIKELY (size() == 0)
// return *this;

const uint32_t wordIdxFrom = bitFrom / BitsPerItem;
const uint32_t wordIdxTo = bitTo / BitsPerItem;

auto getMask = [](uint32_t from, uint32_t to) -> size_type {
const auto diff = to - from;
// Set all bits when asking for the full range
if (diff == BitsPerItem - 1)
return (size_type)-1;

for (uint32_t i = bitFrom; i <= bitTo; i++) {
uint32_t wordIdx = i / BitsPerItem;
uint32_t bitOffset = i % BitsPerItem;
m_data[wordIdx] ^= ((size_type)1 << bitOffset);
return ((size_type(1) << (diff + 1)) - 1) << from;
};

if (wordIdxFrom == wordIdxTo) {
m_data[wordIdxTo] ^= getMask(bitFrom % BitsPerItem, bitTo % BitsPerItem);
} else {
// First word
m_data[wordIdxFrom] ^= getMask(bitFrom % BitsPerItem, BitsPerItem - 1);
// Middle
for (uint32_t i = wordIdxFrom + 1; i <= wordIdxTo - 1; i++)
m_data[i] = ~m_data[i];
// Last word
m_data[wordIdxTo] ^= getMask(0, bitTo % BitsPerItem);
}

return *this;
Expand Down
73 changes: 44 additions & 29 deletions include/gaia/containers/dbitset.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,36 +85,33 @@ namespace gaia {

uint32_t find_next_set_bit(uint32_t pos) const {
value_type wordIndex = pos / dbitset::BitsPerItem;
GAIA_ASSERT(wordIndex < m_bitset.Items());
const auto items = m_bitset.Items();
GAIA_ASSERT(wordIndex < items);
size_type word = 0;

const size_type posInWord = pos % dbitset::BitsPerItem;
if (posInWord < dbitset::BitsPerItem - 1) {
const size_type mask = (size_type(1) << (posInWord + 1)) - 1;
const size_type maskInv = ~mask;
word = m_bitset.m_pData[wordIndex] & maskInv;
const size_type posInWord = pos % dbitset::BitsPerItem + 1;
if GAIA_LIKELY (posInWord < dbitset::BitsPerItem) {
const size_type mask = (size_type(1) << posInWord) - 1;
word = m_bitset.m_pData[wordIndex] & (~mask);
}

// No set bit in the current word, move to the next one
while (word == 0) {
if (wordIndex >= m_bitset.Items() - 1)
GAIA_MSVC_WARNING_PUSH()
GAIA_MSVC_WARNING_DISABLE(4244)
while (true) {
if (word != 0) {
if constexpr (dbitset::BitsPerItem == 32)
return wordIndex * dbitset::BitsPerItem + GAIA_FFS(word) - 1;
else
return wordIndex * dbitset::BitsPerItem + GAIA_FFS64(word) - 1;
}

// No set bit in the current word, move to the next one
if (++wordIndex >= items)
return pos;

word = m_bitset.m_pData[++wordIndex];
word = m_bitset.m_pData[wordIndex];
}

// Process the word
uint32_t fwd = 0;

GAIA_MSVC_WARNING_PUSH()
GAIA_MSVC_WARNING_DISABLE(4244)
if constexpr (dbitset::BitsPerItem == 32)
fwd = GAIA_FFS(word) - 1;
else
fwd = GAIA_FFS64(word) - 1;
GAIA_MSVC_WARNING_POP()

return wordIndex * dbitset::BitsPerItem + fwd;
}

uint32_t find_prev_set_bit(uint32_t pos) const {
Expand Down Expand Up @@ -407,15 +404,33 @@ namespace gaia {
//! Flips all bits from \param bitFrom to \param bitTo (including)
dbitset& flip(uint32_t bitFrom, uint32_t bitTo) {
GAIA_ASSERT(bitFrom <= bitTo);
GAIA_ASSERT(bitFrom < size());
GAIA_ASSERT(bitTo < size());

if GAIA_UNLIKELY (size() == 0)
return *this;

for (uint32_t i = bitFrom; i <= bitTo; i++) {
uint32_t wordIdx = i / BitsPerItem;
uint32_t bitOffset = i % BitsPerItem;
m_pData[wordIdx] ^= ((size_type)1 << bitOffset);
const uint32_t wordIdxFrom = bitFrom / BitsPerItem;
const uint32_t wordIdxTo = bitTo / BitsPerItem;

auto getMask = [](uint32_t from, uint32_t to) -> size_type {
const auto diff = to - from;
// Set all bits when asking for the full range
if (diff == BitsPerItem - 1)
return (size_type)-1;

return ((size_type(1) << (diff + 1)) - 1) << from;
};

if (wordIdxFrom == wordIdxTo) {
m_pData[wordIdxTo] ^= getMask(bitFrom % BitsPerItem, bitTo % BitsPerItem);
} else {
// First word
m_pData[wordIdxFrom] ^= getMask(bitFrom % BitsPerItem, BitsPerItem - 1);
// Middle
for (uint32_t i = wordIdxFrom + 1; i <= wordIdxTo - 1; i++)
m_pData[i] = ~m_pData[i];
// Last word
m_pData[wordIdxTo] ^= getMask(0, bitTo % BitsPerItem);
}

return *this;
Expand Down Expand Up @@ -451,8 +466,8 @@ namespace gaia {

if (HasTrailingBits())
return (m_pData[items] & lastItemMask) == lastItemMask;
else
return m_pData[items] == (size_type)-1;

return m_pData[items] == (size_type)-1;
}

//! Checks if any bit is set
Expand Down
110 changes: 73 additions & 37 deletions single_include/gaia.h
Original file line number Diff line number Diff line change
Expand Up @@ -4647,21 +4647,42 @@ namespace gaia {
//! Flips the bit at the postion \param pos
constexpr void flip(uint32_t pos) {
GAIA_ASSERT(pos < NBits);
m_data[pos / BitsPerItem] ^= ((size_type)1 << (pos % BitsPerItem));
const auto wordIdx = pos / BitsPerItem;
const auto bitIdx = pos % BitsPerItem;
m_data[wordIdx] ^= ((size_type)1 << bitIdx);
}

//! Flips all bits from \param bitFrom to \param bitTo (including)
constexpr bitset& flip(uint32_t bitFrom, uint32_t bitTo) {
GAIA_ASSERT(bitFrom <= bitTo);
GAIA_ASSERT(bitFrom < size());
GAIA_ASSERT(bitTo < size());

if GAIA_UNLIKELY (size() == 0)
return *this;
// The followign can't happen because we always have at least 1 bit
// if GAIA_UNLIKELY (size() == 0)
// return *this;

const uint32_t wordIdxFrom = bitFrom / BitsPerItem;
const uint32_t wordIdxTo = bitTo / BitsPerItem;

for (uint32_t i = bitFrom; i <= bitTo; i++) {
uint32_t wordIdx = i / BitsPerItem;
uint32_t bitOffset = i % BitsPerItem;
m_data[wordIdx] ^= ((size_type)1 << bitOffset);
auto getMask = [](uint32_t from, uint32_t to) -> size_type {
const auto diff = to - from;
// Set all bits when asking for the full range
if (diff == BitsPerItem - 1)
return (size_type)-1;

return ((size_type(1) << (diff + 1)) - 1) << from;
};

if (wordIdxFrom == wordIdxTo) {
m_data[wordIdxTo] ^= getMask(bitFrom % BitsPerItem, bitTo % BitsPerItem);
} else {
// First word
m_data[wordIdxFrom] ^= getMask(bitFrom % BitsPerItem, BitsPerItem - 1);
// Middle
for (uint32_t i = wordIdxFrom + 1; i <= wordIdxTo - 1; i++)
m_data[i] = ~m_data[i];
// Last word
m_data[wordIdxTo] ^= getMask(0, bitTo % BitsPerItem);
}

return *this;
Expand Down Expand Up @@ -5347,36 +5368,33 @@ namespace gaia {

uint32_t find_next_set_bit(uint32_t pos) const {
value_type wordIndex = pos / dbitset::BitsPerItem;
GAIA_ASSERT(wordIndex < m_bitset.Items());
const auto items = m_bitset.Items();
GAIA_ASSERT(wordIndex < items);
size_type word = 0;

const size_type posInWord = pos % dbitset::BitsPerItem;
if (posInWord < dbitset::BitsPerItem - 1) {
const size_type mask = (size_type(1) << (posInWord + 1)) - 1;
const size_type maskInv = ~mask;
word = m_bitset.m_pData[wordIndex] & maskInv;
const size_type posInWord = pos % dbitset::BitsPerItem + 1;
if GAIA_LIKELY (posInWord < dbitset::BitsPerItem) {
const size_type mask = (size_type(1) << posInWord) - 1;
word = m_bitset.m_pData[wordIndex] & (~mask);
}

// No set bit in the current word, move to the next one
while (word == 0) {
if (wordIndex >= m_bitset.Items() - 1)
GAIA_MSVC_WARNING_PUSH()
GAIA_MSVC_WARNING_DISABLE(4244)
while (true) {
if (word != 0) {
if constexpr (dbitset::BitsPerItem == 32)
return wordIndex * dbitset::BitsPerItem + GAIA_FFS(word) - 1;
else
return wordIndex * dbitset::BitsPerItem + GAIA_FFS64(word) - 1;
}

// No set bit in the current word, move to the next one
if (++wordIndex >= items)
return pos;

word = m_bitset.m_pData[++wordIndex];
word = m_bitset.m_pData[wordIndex];
}

// Process the word
uint32_t fwd = 0;

GAIA_MSVC_WARNING_PUSH()
GAIA_MSVC_WARNING_DISABLE(4244)
if constexpr (dbitset::BitsPerItem == 32)
fwd = GAIA_FFS(word) - 1;
else
fwd = GAIA_FFS64(word) - 1;
GAIA_MSVC_WARNING_POP()

return wordIndex * dbitset::BitsPerItem + fwd;
}

uint32_t find_prev_set_bit(uint32_t pos) const {
Expand Down Expand Up @@ -5669,15 +5687,33 @@ namespace gaia {
//! Flips all bits from \param bitFrom to \param bitTo (including)
dbitset& flip(uint32_t bitFrom, uint32_t bitTo) {
GAIA_ASSERT(bitFrom <= bitTo);
GAIA_ASSERT(bitFrom < size());
GAIA_ASSERT(bitTo < size());

if GAIA_UNLIKELY (size() == 0)
return *this;

for (uint32_t i = bitFrom; i <= bitTo; i++) {
uint32_t wordIdx = i / BitsPerItem;
uint32_t bitOffset = i % BitsPerItem;
m_pData[wordIdx] ^= ((size_type)1 << bitOffset);
const uint32_t wordIdxFrom = bitFrom / BitsPerItem;
const uint32_t wordIdxTo = bitTo / BitsPerItem;

auto getMask = [](uint32_t from, uint32_t to) -> size_type {
const auto diff = to - from;
// Set all bits when asking for the full range
if (diff == BitsPerItem - 1)
return (size_type)-1;

return ((size_type(1) << (diff + 1)) - 1) << from;
};

if (wordIdxFrom == wordIdxTo) {
m_pData[wordIdxTo] ^= getMask(bitFrom % BitsPerItem, bitTo % BitsPerItem);
} else {
// First word
m_pData[wordIdxFrom] ^= getMask(bitFrom % BitsPerItem, BitsPerItem - 1);
// Middle
for (uint32_t i = wordIdxFrom + 1; i <= wordIdxTo - 1; i++)
m_pData[i] = ~m_pData[i];
// Last word
m_pData[wordIdxTo] ^= getMask(0, bitTo % BitsPerItem);
}

return *this;
Expand Down Expand Up @@ -5713,8 +5749,8 @@ namespace gaia {

if (HasTrailingBits())
return (m_pData[items] & lastItemMask) == lastItemMask;
else
return m_pData[items] == (size_type)-1;

return m_pData[items] == (size_type)-1;
}

//! Checks if any bit is set
Expand Down
Loading

0 comments on commit 0a58afb

Please sign in to comment.