Skip to content

Commit

Permalink
added dynamic allocation strategy to segmented vector
Browse files Browse the repository at this point in the history
  • Loading branch information
drexlerd committed Jul 7, 2024
1 parent bdb1204 commit b2079cd
Show file tree
Hide file tree
Showing 6 changed files with 176 additions and 253 deletions.
2 changes: 1 addition & 1 deletion benchmarks/iterate_atoms.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ namespace loki::benchmarks

struct AtomAccessResult
{
int atom_identifier;
size_t atom_identifier;
loki::Predicate atom_predicate;
loki::TermList atom_terms;
};
Expand Down
225 changes: 103 additions & 122 deletions include/loki/details/pddl/factories.hpp

Large diffs are not rendered by default.

81 changes: 33 additions & 48 deletions include/loki/details/pddl/factory.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,6 @@ class PDDLFactory
// Use pre-allocated memory to store PDDL object persistent and continuously for improved cache locality.
SegmentedVector<HolderType> m_persistent_vector;

size_t m_count = 0;

void range_check(size_t pos) const
{
if (pos >= size())
Expand All @@ -58,7 +56,10 @@ class PDDLFactory
}

public:
explicit PDDLFactory(size_t elements_per_segment) : m_persistent_vector(SegmentedVector<HolderType>(elements_per_segment)) {}
explicit PDDLFactory(size_t initial_num_element_per_segment = 16, size_t maximum_num_elements_per_segment = 16 * 1024) :
m_persistent_vector(SegmentedVector<HolderType>(initial_num_element_per_segment, maximum_num_elements_per_segment))
{
}
PDDLFactory(const PDDLFactory& other) = delete;
PDDLFactory& operator=(const PDDLFactory& other) = delete;
PDDLFactory(PDDLFactory&& other) = default;
Expand All @@ -72,7 +73,7 @@ class PDDLFactory
/* Construct and insert the element in persistent memory. */

// Ensure that element with identifier i is stored at position i.
size_t identifier = m_count;
size_t identifier = m_uniqueness_set.size();
assert(identifier == m_persistent_vector.size());

// Explicitly call the constructor of T to give exclusive access to the factory.
Expand All @@ -87,8 +88,6 @@ class PDDLFactory
/* Element is unique! */

m_uniqueness_set.emplace(element_ptr);
// Validate the element by increasing the identifier to the next free position
++m_count;
}
else
{
Expand All @@ -98,8 +97,6 @@ class PDDLFactory
// Remove duplicate from vector
m_persistent_vector.pop_back();
}
// Ensure that indexing matches size of uniqueness set.
assert(m_uniqueness_set.size() == m_count);

return element_ptr;
}
Expand All @@ -121,54 +118,42 @@ class PDDLFactory
return &(m_persistent_vector.at(identifier));
}

/**
* Iterators
*/
class const_iterator
{
private:
typename SegmentedVector<HolderType>::const_iterator m_iter;

public:
using difference_type = std::ptrdiff_t;
using value_type = HolderType;
using pointer = HolderType*;
using reference = HolderType&;
using iterator_category = std::forward_iterator_tag;

const_iterator(const SegmentedVector<HolderType>& persistent_vector, bool begin) : m_iter(begin ? persistent_vector.begin() : persistent_vector.end())
{
}
[[nodiscard]] auto begin() const { return m_persistent_vector.begin(); }

[[nodiscard]] decltype(auto) operator*() const { return *m_iter; }

const_iterator& operator++()
{
++m_iter;
return *this;
}
[[nodiscard]] auto end() const { return m_persistent_vector.end(); }

const_iterator operator++(int)
{
const_iterator tmp = *this;
++(*this);
return tmp;
}
[[nodiscard]] const SegmentedVector<HolderType>& get_storage() const { return m_persistent_vector; }

[[nodiscard]] bool operator==(const const_iterator& other) const { return m_iter == other.m_iter; }
/**
* Capacity
*/

[[nodiscard]] bool operator!=(const const_iterator& other) const { return !(*this == other); }
};
[[nodiscard]] size_t size() const { return m_persistent_vector.size(); }
};

[[nodiscard]] const_iterator begin() const { return const_iterator(m_persistent_vector, true); }
template<typename... Ts>
class VariadicPDDLFactory
{
private:
std::tuple<PDDLFactory<Ts>...> m_factories;

[[nodiscard]] const_iterator end() const { return const_iterator(m_persistent_vector, false); }
public:
VariadicPDDLFactory(size_t initial_num_element_per_segment = 16, size_t maximum_num_elements_per_segment = 16 * 1024) :
m_factories(std::make_tuple(PDDLFactory<Ts>(initial_num_element_per_segment, maximum_num_elements_per_segment)...))
{
}

/**
* Capacity
*/
template<typename T>
[[nodiscard]] PDDLFactory<T>& get()
{
return std::get<PDDLFactory<T>>(m_factories);
}

size_t size() const { return m_count; }
template<typename T>
[[nodiscard]] const PDDLFactory<T>& get() const
{
return std::get<PDDLFactory<T>>(m_factories);
}
};

}
Expand Down
113 changes: 35 additions & 78 deletions include/loki/details/utils/segmented_vector.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,27 +35,29 @@ template<typename T>
class SegmentedVector
{
private:
size_t m_elements_per_segment;
size_t m_num_element_per_segment;
size_t m_maximum_num_elements_per_segment;

std::vector<std::vector<T>> m_data;
std::vector<std::vector<T>> m_segments;

std::vector<T*> m_accessor;

size_t m_size;
size_t m_capacity;

void increase_capacity()
{
// Use doubling strategy to make future insertions cheaper.
m_num_element_per_segment = std::min(2 * m_num_element_per_segment, m_maximum_num_elements_per_segment);

// Add an additional vector with capacity N
m_data.resize(m_data.size() + 1);
m_segments.resize(m_segments.size() + 1);
// This reserve is important to avoid reallocations
m_data.back().reserve(m_elements_per_segment);
m_segments.back().reserve(m_num_element_per_segment);
// Increase total capacity
m_capacity += m_elements_per_segment;
m_capacity += m_num_element_per_segment;
}

size_t segment_index(size_t pos) const { return pos / m_elements_per_segment; }

size_t element_index(size_t pos) const { return pos % m_elements_per_segment; }

void range_check(size_t pos) const
{
if (pos >= size())
Expand All @@ -66,7 +68,13 @@ class SegmentedVector
}

public:
explicit SegmentedVector(size_t elements_per_segment) : m_elements_per_segment(elements_per_segment), m_size(0), m_capacity(0) {}
explicit SegmentedVector(size_t initial_num_element_per_segment = 16, size_t maximum_num_elements_per_segment = 16 * 1024) :
m_num_element_per_segment(initial_num_element_per_segment),
m_maximum_num_elements_per_segment(maximum_num_elements_per_segment),
m_size(0),
m_capacity(0)
{
}

/**
* Modifiers
Expand All @@ -78,10 +86,11 @@ class SegmentedVector
{
increase_capacity();
}
auto& segment = m_data[segment_index(size())];

// Take ownership of memory
segment.push_back(std::move(value));
// Take ownership of memory, store address in accessor.
auto& segment = m_segments.back();
auto& element = segment.emplace_back(std::move(value));
m_accessor.push_back(&element);
++m_size;
}

Expand All @@ -94,10 +103,10 @@ class SegmentedVector
increase_capacity();
}

auto& segment = m_data[segment_index(size())];

// Emplace the new element directly in the segment
auto& segment = m_segments.back();
auto& element = segment.emplace_back(std::forward<Args>(args)...);
m_accessor.push_back(&element);
++m_size;

return element;
Expand All @@ -106,8 +115,9 @@ class SegmentedVector
void pop_back()
{
assert(m_size > 0);
auto& segment = m_data[segment_index(size() - 1)];
auto& segment = m_segments.back();
segment.pop_back();
m_accessor.pop_back();
--m_size;
}

Expand All @@ -118,97 +128,44 @@ class SegmentedVector
T& operator[](size_t pos)
{
assert(pos < size());
return m_data[segment_index(pos)][element_index(pos)];
return *m_accessor[pos];
}

const T& operator[](size_t pos) const
{
assert(pos < size());
return m_data[segment_index(pos)][element_index(pos)];
return *m_accessor[pos];
}

T& at(size_t pos)
{
range_check(pos);
return m_data[segment_index(pos)].at(element_index(pos));
return *m_accessor[pos];
}

const T& at(size_t pos) const
{
range_check(pos);
return m_data[segment_index(pos)].at(element_index(pos));
return *m_accessor[pos];
}

T& back()
{
range_check(size() - 1);
return m_data[segment_index(size() - 1)].at(element_index(size() - 1));
return *m_accessor.back();
}

const T& back() const
{
range_check(size() - 1);
return m_data[segment_index(size() - 1)].at(element_index(size() - 1));
return *m_accessor.back();
}

/**
* Iterators
*/
class const_iterator
{
private:
typename std::vector<std::vector<T>>::const_iterator m_outer_iter;
typename std::vector<std::vector<T>>::const_iterator m_outer_end;
typename std::vector<T>::const_iterator m_inner_iter;

public:
using difference_type = std::ptrdiff_t;
using value_type = T;
using pointer = T*;
using reference = T&;
using iterator_category = std::forward_iterator_tag;

const_iterator(const std::vector<std::vector<T>>& data, bool begin) : m_outer_iter(begin ? data.begin() : data.end()), m_outer_end(data.end())
{
// Dereferencing end() is undefined behavior, so we must check before initializing the inner iterator.
if (begin && m_outer_iter != m_outer_end)
{
m_inner_iter = m_outer_iter->begin();
}
}

[[nodiscard]] decltype(auto) operator*() const { return *m_inner_iter; }

const_iterator& operator++()
{
if (++m_inner_iter == m_outer_iter->end())
{
if (++m_outer_iter != m_outer_end)
{
m_inner_iter = m_outer_iter->begin();
}
}
return *this;
}

const_iterator operator++(int)
{
const_iterator tmp = *this;
++(*this);
return tmp;
}

[[nodiscard]] bool operator==(const const_iterator& other) const
{
return m_outer_iter == other.m_outer_iter && (m_outer_iter == m_outer_end || m_inner_iter == other.m_inner_iter);
}

[[nodiscard]] bool operator!=(const const_iterator& other) const { return !(*this == other); }
};
auto begin() const { return m_accessor.begin(); }

[[nodiscard]] const_iterator begin() const { return const_iterator(m_data, true); }
auto end() const { return m_accessor.end(); }

[[nodiscard]] const_iterator end() const { return const_iterator(m_data, false); }
size_t num_segments() const { return m_segments.size(); }

/**
* Capacity
Expand Down
4 changes: 2 additions & 2 deletions tests/unit/pddl/factory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ TEST(LokiTests, FactoryIteratorTest)
auto objects = ObjectList {};
for (const auto& object : factory)
{
objects.push_back(&object);
objects.push_back(object);
}

EXPECT_EQ(objects.size(), 3);
Expand All @@ -49,7 +49,7 @@ TEST(LokiTests, FactoryIteratorEmptyTest)
auto objects = ObjectList {};
for (const auto& object : factory)
{
objects.push_back(&object);
objects.push_back(object);
}

EXPECT_EQ(objects.size(), 0);
Expand Down
4 changes: 2 additions & 2 deletions tests/unit/utils/segmented_vector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ namespace loki::domain::tests

TEST(LokiTests, SegmentedVectorTest)
{
SegmentedVector<int> vec(2);
SegmentedVector<int> vec(1);
EXPECT_EQ(vec.size(), 0);
EXPECT_EQ(vec.capacity(), 0);

Expand All @@ -40,7 +40,7 @@ TEST(LokiTests, SegmentedVectorTest)
vec.push_back(0);
EXPECT_EQ(vec.size(), 3);
EXPECT_EQ(vec[2], 0);
EXPECT_EQ(vec.capacity(), 4);
EXPECT_EQ(vec.capacity(), 6);
}

}

0 comments on commit b2079cd

Please sign in to comment.