Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions src/openvic-simulation/core/memory/TunableVector.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#pragma once

#include <foonathan/memory/default_allocator.hpp>
#include <foonathan/memory/std_allocator.hpp>

#include "openvic-simulation/core/memory/MemoryTracker.hpp"
#include "openvic-simulation/core/stl/containers/TunableVector.hpp"

namespace OpenVic::memory {
template<
typename T, ::OpenVic::stl::_detail::tunable_growth_trait GrowthTrait = ::OpenVic::stl::default_growth_traits,
class RawAllocator = foonathan::memory::default_allocator>
using tunable_vector =
::OpenVic::stl::tunable_vector<T, GrowthTrait, foonathan::memory::std_allocator<T, tracker<RawAllocator>>>;
}
357 changes: 357 additions & 0 deletions src/openvic-simulation/core/stl/containers/TunableVector.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,357 @@
#pragma once

#include <algorithm>
#include <concepts>
#include <cstddef>
#include <initializer_list>
#include <iterator>
#include <memory>
#include <ranges>
#include <type_traits>
#include <utility>
#include <vector>

#include <range/v3/algorithm/move.hpp>

#include "openvic-simulation/core/Assert.hpp"

namespace OpenVic::stl {
template<size_t Num, size_t Denom, size_t InitAllocSize = 0>
struct growth_traits {
static constexpr std::integral_constant<size_t, Num> numerator {};
static constexpr std::integral_constant<size_t, Denom> denominator {};
static constexpr std::integral_constant<size_t, InitAllocSize> initial_allocation_size {};
};

// Uses approximated golden ratio (1.617...)
using default_growth_traits = growth_traits<55, 34>;

namespace _detail {
template<typename T>
concept tunable_growth_trait = requires() {
{ T::numerator } -> std::convertible_to<size_t>;
{ T::denominator } -> std::convertible_to<size_t>;
{ T::initial_allocation_size } -> std::convertible_to<size_t>;
};

template<typename Range, typename T>
concept _compatible_range =
std::ranges::input_range<Range> && std::convertible_to<std::ranges::range_reference_t<Range>, T>;
}

template<
typename T, _detail::tunable_growth_trait GrowthTrait = default_growth_traits, typename Allocator = std::allocator<T>>
class tunable_vector {
public:
using value_type = T;
using allocator_type = Allocator;
using container_type = std::vector<value_type, allocator_type>;
using size_type = typename container_type::size_type;
using difference_type = typename container_type::difference_type;
using reference = typename container_type::reference;
using const_reference = typename container_type::const_reference;
using pointer = typename container_type::pointer;
using const_pointer = typename container_type::const_pointer;
using iterator = typename container_type::iterator;
using const_iterator = typename container_type::const_iterator;
using reverse_iterator = typename container_type::reverse_iterator;
using const_reverse_iterator = typename container_type::const_reverse_iterator;
using growth_trait = GrowthTrait;

constexpr tunable_vector() : _container() {}
explicit constexpr tunable_vector(allocator_type const& alloc) : _container(alloc) {}
explicit constexpr tunable_vector(size_type count, allocator_type const& alloc = allocator_type {})
: _container(count, alloc) {}
constexpr tunable_vector(size_type count, value_type const& value, allocator_type const& alloc = allocator_type {})
: _container(count, value, alloc) {}
template<typename InputIt>
constexpr tunable_vector(InputIt first, InputIt last, allocator_type const& alloc = allocator_type {})
: _container(first, last, alloc) {}

constexpr tunable_vector(container_type const& other) : _container { other } {}
constexpr tunable_vector(container_type&& other) : _container { std::move(other) } {}

constexpr tunable_vector(container_type const& other, allocator_type const& alloc) : _container { other, alloc } {}
constexpr tunable_vector(container_type&& other, allocator_type const& alloc)
: _container { std::move(other), alloc } {}

constexpr tunable_vector(tunable_vector const& other) : _container { other._container } {}
constexpr tunable_vector(tunable_vector&& other) : _container { std::move(other._container) } {}

constexpr tunable_vector(tunable_vector const& other, allocator_type const& alloc)
: _container { other._container, alloc } {}
constexpr tunable_vector(tunable_vector&& other, allocator_type const& alloc)
: _container { std::move(other._container), alloc } {}

template<typename OtherGrowth>
constexpr tunable_vector(tunable_vector<T, OtherGrowth, Allocator> const& other) : _container { other._container } {}
template<typename OtherGrowth>
constexpr tunable_vector(tunable_vector<T, OtherGrowth, Allocator>&& other)
: _container { std::move(other._container) } {}

template<typename OtherGrowth>
constexpr tunable_vector(tunable_vector<T, OtherGrowth, Allocator> const& other, allocator_type const& alloc)
: _container { other._container, alloc } {}
template<typename OtherGrowth>
constexpr tunable_vector(tunable_vector<T, OtherGrowth, Allocator>&& other, allocator_type const& alloc)
: _container { std::move(other._container), alloc } {}

constexpr tunable_vector(std::initializer_list<value_type> init, allocator_type const& alloc = allocator_type {})
: _container(init, alloc) {}

constexpr tunable_vector& operator=(container_type const& other) {
_container = other;
return *this;
}
constexpr tunable_vector& operator=(container_type&& other) {
_container = std::move(other);
return *this;
}

constexpr tunable_vector& operator=(tunable_vector const& other) {
_container = other._container;
return *this;
}
constexpr tunable_vector& operator=(tunable_vector&& other) {
_container = std::move(other._container);
return *this;
}

tunable_vector& operator=(std::initializer_list<value_type> ilist) {
_container = ilist;
return *this;
}

constexpr reference operator[](const size_type pos) {
OV_HARDEN_ASSERT_ACCESS(pos, "operator[]");
return _container[pos];
}

constexpr const_reference operator[](const size_type pos) const {
OV_HARDEN_ASSERT_ACCESS(pos, "operator[]");
return _container[pos];
}

constexpr reference front() {
OV_HARDEN_ASSERT_NONEMPTY("front");
return _container[0];
}

constexpr const_reference front() const {
OV_HARDEN_ASSERT_NONEMPTY("front");
return _container[0];
}

constexpr reference back() {
OV_HARDEN_ASSERT_NONEMPTY("back");
return _container[size() - 1];
}

constexpr const_reference back() const {
OV_HARDEN_ASSERT_NONEMPTY("back");
return _container[size() - 1];
}

constexpr value_type* data() {
return _container.data();
}

constexpr value_type const* data() const {
return _container.data();
}

constexpr iterator begin() {
return _container.begin();
}

constexpr const_iterator begin() const {
return _container.begin();
}

constexpr const_iterator cbegin() const {
return _container.cbegin();
}

constexpr iterator end() {
return _container.end();
}

constexpr const_iterator end() const {
return _container.end();
}

constexpr const_iterator cend() const {
return _container.cend();
}

constexpr reverse_iterator rbegin() {
return _container.rbegin();
}

constexpr const_reverse_iterator rbegin() const {
return _container.rbegin();
}

constexpr const_reverse_iterator crbegin() const {
return _container.crbegin();
}

constexpr reverse_iterator rend() {
return _container.rend();
}

constexpr const_reverse_iterator rend() const {
return _container.rend();
}

constexpr const_reverse_iterator crend() const {
return _container.crend();
}

constexpr bool empty() const {
return _container.empty();
}

constexpr size_type size() const {
return _container.size();
}

constexpr size_type max_size() const {
return _container.max_size();
}

constexpr size_type capacity() const {
return _container.capacity();
}

constexpr void shrink_to_fit() {
_container.shrink_to_fit();
}

constexpr void clear() {
_container.clear();
}

constexpr iterator erase(const_iterator pos) {
return _container.erase(pos);
}

constexpr iterator erase(const_iterator first, const_iterator last) {
return _container.erase(first, last);
}

constexpr void swap(tunable_vector& vector) {
_container.swap(vector._container);
}

constexpr allocator_type get_allocator() const {
return _container.get_allocator();
}

constexpr void push_back(value_type const& value) {
if (size() == capacity()) {
reserve_minimum(capacity() + 1);
}
_container.push_back(value);
}

constexpr void push_back(value_type&& value) {
if (size() == capacity()) {
reserve_minimum(capacity() + 1);
}
_container.push_back(std::move(value));
}

template<typename... Args>
constexpr reference emplace_back(Args&&... args) {
if (size() == capacity()) {
reserve_minimum(capacity() + 1);
}
return _container.emplace_back(std::forward<Args>(args)...);
}

template<_detail::_compatible_range<T> RangeT>
constexpr void append_range(RangeT&& range) {
if constexpr (std::ranges::forward_range<RangeT> || std::ranges::sized_range<RangeT>) {
reserve_minimum(std::ranges::distance(range));
ranges::move(range, std::back_inserter(*this));
} else {
auto first = std::ranges::begin(range);
const auto last = std::ranges::end(range);

for (size_type free = capacity() - size(); first != last && free != size_type {};
std::ranges::advance(first, 1), --free) {
emplace_back(*first);
}

if (first == last) {
return;
}

tunable_vector<value_type> tmp { get_allocator() };
for (; first != last; std::ranges::advance(first, 1)) {
tmp.emplace_back(*first);
}
std::ranges::subrange subrange(std::make_move_iterator(tmp.begin()), std::make_move_iterator(tmp.end()));
append_range(subrange);
}
}

constexpr void resize(size_type count) {
if (count > capacity()) {
reserve_minimum(count);
Comment thread
Spartan322 marked this conversation as resolved.
}
_container.resize(count);
}

constexpr void reserve_minimum(size_type count) {
if (count > capacity()) {
_container.reserve(_get_capacity_for(count));
}
}

constexpr void reserve_exact(size_type count) {
_container.reserve(count);
}

constexpr container_type const& container() const {
return _container;
}

constexpr container_type&& release() && {
return std::move(_container);
}

private:
container_type _container;

constexpr size_type _get_capacity_for(const size_type value) const {
const size_type max = max_size();
if (max < value) {
return max;
}

if (value < capacity()) {
return value;
}

const size_type old_capacity = capacity();
const size_type new_capacity = old_capacity * growth_trait::numerator / growth_trait::denominator;

if (new_capacity > max ||
(old_capacity != 0 && new_capacity / growth_trait::numerator < old_capacity / growth_trait::denominator)) {
return max;
}

if (old_capacity == size_type {} && new_capacity < growth_trait::initial_allocation_size) {
return growth_trait::initial_allocation_size;
}

if (value > new_capacity) {
return value;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems wrong.
Say you reserve for value and fill it to or near value then add 1-3 elements, it would have to reallocate again.
It would make more sense if it:

  • reserves for value + buffer size
  • reserves for value x factor
  • reserves for smallest factor^n that fits value (+ buffer)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only specifically in the case that you reserve more than the next factor size, this is what all the standard libraries will do with resize.

}

return new_capacity;
}
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ namespace OpenVic {
ProvinceInstance& location,
const fixed_point_t max_cost_multiplier
);

private:
struct artisan_tick_handler {
private:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ namespace OpenVic {
const effect_t effect_type;
const fixed_point_t effect_multiplier;
const fixed_point_t amount;

constexpr Job(
const pop_type_index_t new_pop_type_index,
const effect_t new_effect_type,
Expand Down Expand Up @@ -150,4 +150,4 @@ namespace OpenVic {

bool parse_scripts(DefinitionManager const& definition_manager);
};
}
}
Loading
Loading