Speed up the IndexSet combining operations

This commit is contained in:
Thomas Goyne 2016-03-15 14:04:21 -07:00
parent fdeb80f970
commit 88a3b6ed00
3 changed files with 135 additions and 23 deletions

View File

@ -92,6 +92,9 @@ size_t IndexSet::add_shifted(size_t index)
void IndexSet::add_shifted_by(IndexSet const& shifted_by, IndexSet const& values)
{
if (values.empty())
return;
#ifdef REALM_DEBUG
size_t expected = std::distance(as_indexes().begin(), as_indexes().end());
for (auto index : values.as_indexes()) {
@ -100,19 +103,39 @@ void IndexSet::add_shifted_by(IndexSet const& shifted_by, IndexSet const& values
}
#endif
auto it = shifted_by.begin(), end = shifted_by.end();
size_t shift = 0;
auto old_ranges = move(m_ranges);
m_ranges.reserve(std::max(old_ranges.size(), values.size()));
auto old_it = old_ranges.cbegin(), old_end = old_ranges.cend();
auto shift_it = shifted_by.m_ranges.cbegin(), shift_end = shifted_by.m_ranges.cend();
size_t skip_until = 0;
size_t old_shift = 0;
size_t new_shift = 0;
for (size_t index : values.as_indexes()) {
for (; it != end && it->first <= index; ++it) {
shift += it->second - it->first;
skip_until = it->second;
for (; shift_it != shift_end && shift_it->first <= index; ++shift_it) {
new_shift += shift_it->second - shift_it->first;
skip_until = shift_it->second;
}
if (index >= skip_until) {
REALM_ASSERT(index >= shift);
add_shifted(index - shift);
++shift;
if (index < skip_until)
continue;
for (; old_it != old_end && old_it->first <= index - new_shift + old_shift; ++old_it) {
for (size_t i = old_it->first; i < old_it->second; ++i)
add_back(i);
old_shift += old_it->second - old_it->first;
}
REALM_ASSERT(index >= new_shift);
add_back(index - new_shift + old_shift);
}
if (old_it != old_end) {
if (!empty() && old_it->first == m_ranges.back().second) {
m_ranges.back().second = old_it->second;
++old_it;
}
copy(old_it, old_end, back_inserter(m_ranges));
}
REALM_ASSERT_DEBUG(std::distance(as_indexes().begin(), as_indexes().end()) == expected);
@ -151,9 +174,33 @@ void IndexSet::insert_at(size_t index, size_t count)
void IndexSet::insert_at(IndexSet const& positions)
{
for (auto range : positions) {
insert_at(range.first, range.second - range.first);
if (positions.empty())
return;
if (empty()) {
m_ranges = positions.m_ranges;
return;
}
auto old_ranges = move(m_ranges);
m_ranges.reserve(std::max(m_ranges.size(), positions.m_ranges.size()));
IndexIterator begin1 = old_ranges.cbegin(), begin2 = positions.m_ranges.cbegin();
IndexIterator end1 = old_ranges.cend(), end2 = positions.m_ranges.cend();
size_t shift = 0;
while (begin1 != end1 && begin2 != end2) {
if (*begin1 + shift < *begin2) {
add_back(*begin1++ + shift);
}
else {
++shift;
add_back(*begin2++);
}
}
for (; begin1 != end1; ++begin1)
add_back(*begin1 + shift);
for (; begin2 != end2; ++begin2)
add_back(*begin2);
}
void IndexSet::shift_for_insert_at(size_t index, size_t count)
@ -179,8 +226,34 @@ void IndexSet::shift_for_insert_at(size_t index, size_t count)
void IndexSet::shift_for_insert_at(realm::IndexSet const& values)
{
for (auto range : values)
shift_for_insert_at(range.first, range.second - range.first);
if (values.empty())
return;
size_t shift = 0;
auto it = find(values.begin()->first);
for (auto range : values) {
for (; it != m_ranges.end() && it->second + shift <= range.first; ++it) {
it->first += shift;
it->second += shift;
}
if (it == m_ranges.end())
return;
if (it->first + shift < range.first) {
// split the range so that we can exclude `index`
auto old_second = it->second;
it->first += shift;
it->second = range.first;
it = m_ranges.insert(it + 1, {range.first - shift, old_second});
}
shift += range.second - range.first;
}
for (; it != m_ranges.end(); ++it) {
it->first += shift;
it->second += shift;
}
}
void IndexSet::erase_at(size_t index)
@ -190,11 +263,34 @@ void IndexSet::erase_at(size_t index)
do_erase(it, index);
}
void IndexSet::erase_at(realm::IndexSet const& values)
void IndexSet::erase_at(IndexSet const& positions)
{
if (empty() || positions.empty())
return;
auto old_ranges = move(m_ranges);
m_ranges.reserve(std::max(m_ranges.size(), positions.m_ranges.size()));
IndexIterator begin1 = old_ranges.cbegin(), begin2 = positions.m_ranges.cbegin();
IndexIterator end1 = old_ranges.cend(), end2 = positions.m_ranges.cend();
size_t shift = 0;
for (auto index : values.as_indexes())
erase_at(index - shift++);
while (begin1 != end1 && begin2 != end2) {
if (*begin1 < *begin2) {
add_back(*begin1++ - shift);
}
else if (*begin1 == *begin2) {
++shift;
++begin1;
++begin2;
}
else {
++shift;
++begin2;
}
}
for (; begin1 != end1; ++begin1)
add_back(*begin1 - shift);
}
size_t IndexSet::erase_and_unshift(size_t index)
@ -303,6 +399,18 @@ void IndexSet::clear()
m_ranges.clear();
}
void IndexSet::add_back(size_t index)
{
if (m_ranges.empty())
m_ranges.push_back({index, index + 1});
else if (m_ranges.back().second == index)
++m_ranges.back().second;
else {
REALM_ASSERT_DEBUG(m_ranges.back().second < index);
m_ranges.push_back({index, index + 1});
}
}
IndexSet::iterator IndexSet::do_add(iterator it, size_t index)
{
verify();

View File

@ -93,13 +93,14 @@ public:
void verify() const noexcept;
// An iterator over the indivual indices in the set rather than the ranges
class IndexInterator : public std::iterator<std::forward_iterator_tag, size_t> {
class IndexIterator : public std::iterator<std::forward_iterator_tag, size_t> {
public:
IndexInterator(IndexSet::const_iterator it) : m_iterator(it) { }
IndexIterator(IndexSet::const_iterator it) : m_iterator(it) { }
size_t operator*() const { return m_iterator->first + m_offset; }
bool operator!=(IndexInterator const& it) const { return m_iterator != it.m_iterator; }
bool operator==(IndexIterator const& it) const { return m_iterator == it.m_iterator; }
bool operator!=(IndexIterator const& it) const { return m_iterator != it.m_iterator; }
IndexInterator& operator++()
IndexIterator& operator++()
{
++m_offset;
if (m_iterator->first + m_offset == m_iterator->second) {
@ -109,7 +110,7 @@ public:
return *this;
}
IndexInterator operator++(int)
IndexIterator operator++(int)
{
auto value = *this;
++*this;
@ -124,7 +125,7 @@ public:
class IndexIteratableAdaptor {
public:
using value_type = size_t;
using iterator = IndexInterator;
using iterator = IndexIterator;
using const_iterator = iterator;
const_iterator begin() const { return m_index_set.begin(); }
@ -150,6 +151,9 @@ private:
iterator do_add(iterator pos, size_t index);
void do_erase(iterator it, size_t index);
iterator do_remove(iterator it, size_t index, size_t count);
// Add an index which must be greater than the largest index in the set
void add_back(size_t index);
};
} // namespace realm