2
0
mirror of https://github.com/boostorg/math.git synced 2026-02-24 04:02:18 +00:00

reverted linear allocator due to issue

This commit is contained in:
Maksym Zhelyeznyakov
2025-10-17 19:41:18 +02:00
parent 8078eba9d7
commit d1b6d28385
2 changed files with 355 additions and 322 deletions

View File

@@ -23,407 +23,433 @@ namespace detail {
template<typename allocator_type, size_t buffer_size>
class flat_linear_allocator_iterator
{
/**
/**
* @brief enables iterating over linear allocator with
* c++ iterators
*/
public:
using raw_allocator_type = std::remove_const_t<allocator_type>;
using value_type = typename allocator_type::value_type;
using pointer = typename allocator_type::value_type *;
using const_ptr_type = const value_type *;
using reference = typename allocator_type::value_type &;
using const_reference_type = const value_type &;
using iterator_category = std::random_access_iterator_tag;
using difference_type = ptrdiff_t;
using raw_allocator_type = std::remove_const_t<allocator_type>;
using value_type = typename allocator_type::value_type;
using pointer = typename allocator_type::value_type*;
using const_ptr_type = const value_type*;
using reference = typename allocator_type::value_type&;
using const_reference_type = const value_type&;
using iterator_category = std::random_access_iterator_tag;
using difference_type = ptrdiff_t;
private:
const allocator_type *storage_ = nullptr;
size_t index_ = 0;
size_t begin_ = 0;
size_t end_ = 0;
const allocator_type* storage_ = nullptr;
size_t index_ = 0;
size_t begin_ = 0;
size_t end_ = 0;
public:
flat_linear_allocator_iterator() = default;
flat_linear_allocator_iterator() = default;
explicit flat_linear_allocator_iterator(allocator_type *storage, size_t index)
: storage_(storage)
, index_(index)
, begin_(0)
, end_(storage->size())
{}
explicit flat_linear_allocator_iterator(allocator_type* storage, size_t index)
: storage_(storage)
, index_(index)
, begin_(0)
, end_(storage->size())
{
}
explicit flat_linear_allocator_iterator(allocator_type *storage,
size_t index,
size_t begin,
size_t end)
: storage_(storage)
, index_(index)
, begin_(begin)
, end_(end)
{}
explicit flat_linear_allocator_iterator(allocator_type* storage,
size_t index,
size_t begin,
size_t end)
: storage_(storage)
, index_(index)
, begin_(begin)
, end_(end)
{
}
explicit flat_linear_allocator_iterator(const allocator_type *storage, size_t index)
: storage_(storage)
, index_(index)
, begin_(0)
, end_(storage->size())
{}
explicit flat_linear_allocator_iterator(const allocator_type* storage,
size_t index)
: storage_(storage)
, index_(index)
, begin_(0)
, end_(storage->size())
{
}
explicit flat_linear_allocator_iterator(const allocator_type *storage,
size_t index,
size_t begin,
size_t end)
: storage_(storage)
, index_(index)
, begin_(begin)
, end_(end)
{}
reference operator*()
{
BOOST_MATH_ASSERT(index_ >= begin_ && index_ < end_);
return (*storage_->data_[index_ / buffer_size])[index_ % buffer_size];
}
explicit flat_linear_allocator_iterator(const allocator_type* storage,
size_t index,
size_t begin,
size_t end)
: storage_(storage)
, index_(index)
, begin_(begin)
, end_(end)
{
}
reference operator*()
{
BOOST_MATH_ASSERT(index_ >= begin_ && index_ < end_);
return (*storage_->data_[index_ / buffer_size])[index_ % buffer_size];
}
const_reference_type operator*() const
{
BOOST_MATH_ASSERT(index_ >= begin_ && index_ < end_);
return (*storage_->data_[index_ / buffer_size])[index_ % buffer_size];
}
const_reference_type operator*() const
{
BOOST_MATH_ASSERT(index_ >= begin_ && index_ < end_);
return (*storage_->data_[index_ / buffer_size])[index_ % buffer_size];
}
pointer operator->()
{
BOOST_MATH_ASSERT(index_ >= begin_ && index_ < end_);
return &operator*();
}
pointer operator->()
{
BOOST_MATH_ASSERT(index_ >= begin_ && index_ < end_);
return &operator*();
}
const_ptr_type operator->() const
{
BOOST_MATH_ASSERT(index_ >= begin_ && index_ < end_);
return &operator*();
}
flat_linear_allocator_iterator &operator++()
{
++index_;
return *this;
}
const_ptr_type operator->() const
{
BOOST_MATH_ASSERT(index_ >= begin_ && index_ < end_);
return &operator*();
}
flat_linear_allocator_iterator& operator++()
{
++index_;
return *this;
}
flat_linear_allocator_iterator operator++(int)
{
auto tmp = *this;
++(*this);
return tmp;
}
flat_linear_allocator_iterator operator++(int)
{
auto tmp = *this;
++(*this);
return tmp;
}
flat_linear_allocator_iterator &operator--()
{
--index_;
return *this;
}
flat_linear_allocator_iterator& operator--()
{
--index_;
return *this;
}
flat_linear_allocator_iterator operator--(int)
{
auto tmp = *this;
--(*this);
return tmp;
}
flat_linear_allocator_iterator operator--(int)
{
auto tmp = *this;
--(*this);
return tmp;
}
bool operator==(const flat_linear_allocator_iterator &other) const
{
return index_ == other.index_ && storage_ == other.storage_;
}
bool operator==(const flat_linear_allocator_iterator& other) const
{
return index_ == other.index_ && storage_ == other.storage_;
}
bool operator!=(const flat_linear_allocator_iterator &other) const { return !(*this == other); }
bool operator!=(const flat_linear_allocator_iterator& other) const
{
return !(*this == other);
}
flat_linear_allocator_iterator operator+(difference_type n) const
{
return flat_linear_allocator_iterator(storage_, index_ + static_cast<size_t>(n), begin_, end_);
}
flat_linear_allocator_iterator operator+(difference_type n) const
{
return flat_linear_allocator_iterator(
storage_, index_ + static_cast<size_t>(n), begin_, end_);
}
flat_linear_allocator_iterator &operator+=(difference_type n)
{
index_ += n;
return *this;
}
flat_linear_allocator_iterator& operator+=(difference_type n)
{
index_ += n;
return *this;
}
flat_linear_allocator_iterator operator-(difference_type n) const
{
return flat_linear_allocator_iterator(storage_, index_ - n, begin_, end_);
}
flat_linear_allocator_iterator &operator-=(difference_type n)
{
index_ -= n;
return *this;
}
flat_linear_allocator_iterator operator-(difference_type n) const
{
return flat_linear_allocator_iterator(storage_, index_ - n, begin_, end_);
}
flat_linear_allocator_iterator& operator-=(difference_type n)
{
index_ -= n;
return *this;
}
difference_type operator-(const flat_linear_allocator_iterator &other) const
{
return static_cast<difference_type>(index_) - static_cast<difference_type>(other.index_);
}
difference_type operator-(const flat_linear_allocator_iterator& other) const
{
return static_cast<difference_type>(index_) -
static_cast<difference_type>(other.index_);
}
reference operator[](difference_type n) { return *(*this + n); }
reference operator[](difference_type n) { return *(*this + n); }
const_reference_type operator[](difference_type n) const { return *(*this + n); }
const_reference_type operator[](difference_type n) const
{
return *(*this + n);
}
bool operator<(const flat_linear_allocator_iterator &other) const
{
return index_ < other.index_;
}
bool operator<(const flat_linear_allocator_iterator& other) const
{
return index_ < other.index_;
}
bool operator>(const flat_linear_allocator_iterator &other) const
{
return index_ > other.index_;
}
bool operator>(const flat_linear_allocator_iterator& other) const
{
return index_ > other.index_;
}
bool operator<=(const flat_linear_allocator_iterator &other) const
{
return index_ <= other.index_;
}
bool operator<=(const flat_linear_allocator_iterator& other) const
{
return index_ <= other.index_;
}
bool operator>=(const flat_linear_allocator_iterator &other) const
{
return index_ >= other.index_;
}
bool operator>=(const flat_linear_allocator_iterator& other) const
{
return index_ >= other.index_;
}
bool operator!() const noexcept { return storage_ == nullptr; }
bool operator!() const noexcept { return storage_ == nullptr; }
};
/* memory management helps for tape */
template<typename RealType, size_t buffer_size>
class flat_linear_allocator
{
/** @brief basically a vector<array<T*, size>>
/** @brief basically a vector<array<T*, size>>
* intended to work like a vector that allocates memory in chunks
* and doesn't invalidate references
* */
public:
// store vector of unique pointers to arrays
// to avoid vector reference invalidation
using buffer_type = std::array<RealType, buffer_size>;
using buffer_ptr = std::unique_ptr<std::array<RealType, buffer_size>>;
// store vector of unique pointers to arrays
// to avoid vector reference invalidation
using buffer_type = std::array<RealType, buffer_size>;
using buffer_ptr = std::unique_ptr<std::array<RealType, buffer_size>>;
private:
std::vector<buffer_ptr> data_;
size_t total_size_ = 0;
std::vector<size_t> checkpoints_; //{0};
std::vector<buffer_ptr> data_;
size_t total_size_ = 0;
std::vector<size_t> checkpoints_; //{0};
public:
friend class flat_linear_allocator_iterator<flat_linear_allocator<RealType, buffer_size>,
buffer_size>;
friend class flat_linear_allocator_iterator<const flat_linear_allocator<RealType, buffer_size>,
buffer_size>;
using value_type = RealType;
using iterator
= flat_linear_allocator_iterator<flat_linear_allocator<RealType, buffer_size>, buffer_size>;
using const_iterator
= flat_linear_allocator_iterator<const flat_linear_allocator<RealType, buffer_size>,
buffer_size>;
friend class flat_linear_allocator_iterator<
flat_linear_allocator<RealType, buffer_size>,
buffer_size>;
friend class flat_linear_allocator_iterator<
const flat_linear_allocator<RealType, buffer_size>,
buffer_size>;
using value_type = RealType;
using iterator =
flat_linear_allocator_iterator<flat_linear_allocator<RealType, buffer_size>,
buffer_size>;
using const_iterator = flat_linear_allocator_iterator<
const flat_linear_allocator<RealType, buffer_size>,
buffer_size>;
size_t buffer_id() const noexcept { return total_size_ / buffer_size; }
size_t item_id() const noexcept { return total_size_ % buffer_size; }
size_t buffer_id() const noexcept { return total_size_ / buffer_size; }
size_t item_id() const noexcept { return total_size_ % buffer_size; }
private:
void allocate_buffer()
{
data_.emplace_back(std::make_unique<buffer_type>());
}
void allocate_buffer()
{
data_.emplace_back(std::make_unique<buffer_type>());
}
public:
flat_linear_allocator() { allocate_buffer(); }
flat_linear_allocator(const flat_linear_allocator &) = delete;
flat_linear_allocator &operator=(const flat_linear_allocator &) = delete;
flat_linear_allocator(flat_linear_allocator &&) = delete;
flat_linear_allocator &operator=(flat_linear_allocator &&) = delete;
~flat_linear_allocator()
{
destroy_all();
data_.clear();
}
flat_linear_allocator() { allocate_buffer(); }
flat_linear_allocator(const flat_linear_allocator&) = delete;
flat_linear_allocator& operator=(const flat_linear_allocator&) = delete;
flat_linear_allocator(flat_linear_allocator&&) = delete;
flat_linear_allocator& operator=(flat_linear_allocator&&) = delete;
~flat_linear_allocator()
{
destroy_all();
data_.clear();
}
void destroy_all()
{
for (size_t i = 0; i < total_size_; ++i) {
size_t bid = i / buffer_size;
size_t iid = i % buffer_size;
(*data_[bid])[iid].~RealType();
}
void destroy_all()
{
for (size_t i = 0; i < total_size_; ++i) {
size_t bid = i / buffer_size;
size_t iid = i % buffer_size;
(*data_[bid])[iid].~RealType();
}
/** @brief
}
/** @brief
* helper functions to clear tape and create block in tape
*/
void clear()
{
data_.clear();
total_size_ = 0;
checkpoints_.clear();
allocate_buffer();
void clear()
{
data_.clear();
total_size_ = 0;
checkpoints_.clear();
allocate_buffer();
}
// doesn't delete anything, only sets the current index to zero
void reset() { total_size_ = 0; }
void rewind() { total_size_ = 0; };
// adds current index as a checkpoint to be able to walk back to
void add_checkpoint()
{
if (total_size_ > 0) {
checkpoints_.push_back(total_size_ - 1);
} else {
checkpoints_.push_back(0);
}
};
// doesn't delete anything, only sets the current index to zero
void reset() { total_size_ = 0; }
void rewind() { total_size_ = 0; };
// adds current index as a checkpoint to be able to walk back to
void add_checkpoint()
{
if (total_size_ > 0) {
checkpoints_.push_back(total_size_); //- 1);
} else {
checkpoints_.push_back(0);
}
};
/** @brief clears all checkpoints
/** @brief clears all checkpoints
* */
void reset_checkpoints() { checkpoints_.clear(); }
void reset_checkpoints() { checkpoints_.clear(); }
void rewind_to_last_checkpoint() { total_size_ = checkpoints_.back(); }
void rewind_to_checkpoint_at(size_t index) { total_size_ = checkpoints_[index]; }
void rewind_to_last_checkpoint() { total_size_ = checkpoints_.back(); }
void rewind_to_checkpoint_at(size_t index)
{
total_size_ = checkpoints_[index];
}
void fill(const RealType &val)
{
for (size_t i = 0; i < total_size_; ++i) {
size_t bid = i / buffer_size;
size_t iid = i % buffer_size;
(*data_[bid])[iid] = val;
}
void fill(const RealType& val)
{
for (size_t i = 0; i < total_size_; ++i) {
size_t bid = i / buffer_size;
size_t iid = i % buffer_size;
(*data_[bid])[iid] = val;
}
}
/** @brief emplaces back object at the end of the
/** @brief emplaces back object at the end of the
* data structure, calls default constructor */
iterator emplace_back()
{
if (item_id() == 0 && total_size_ != 0) {
allocate_buffer();
}
size_t bid = buffer_id();
size_t iid = item_id();
iterator emplace_back()
{
if (item_id() == 0 && total_size_ != 0) {
allocate_buffer();
}
size_t bid = buffer_id();
size_t iid = item_id();
RealType *ptr = &(*data_[bid])[iid];
new (ptr) RealType();
++total_size_;
return iterator(this, total_size_ - 1);
};
RealType* ptr = &(*data_[bid])[iid];
new (ptr) RealType();
++total_size_;
return iterator(this, total_size_ - 1);
};
/** @brief, emplaces back object at end of data structure,
/** @brief, emplaces back object at end of data structure,
* passes arguments to constructor */
template<typename... Args>
iterator emplace_back(Args &&...args)
{
if (item_id() == 0 && total_size_ != 0) {
allocate_buffer();
}
BOOST_MATH_ASSERT(buffer_id() < data_.size());
BOOST_MATH_ASSERT(item_id() < buffer_size);
RealType *ptr = &(*data_[buffer_id()])[item_id()];
new (ptr) RealType(std::forward<Args>(args)...);
++total_size_;
return iterator(this, total_size_ - 1);
template<typename... Args>
iterator emplace_back(Args&&... args)
{
if (item_id() == 0 && total_size_ != 0) {
allocate_buffer();
}
/** @brief default constructs n objects at end of
BOOST_MATH_ASSERT(buffer_id() < data_.size());
BOOST_MATH_ASSERT(item_id() < buffer_size);
RealType* ptr = &(*data_[buffer_id()])[item_id()];
new (ptr) RealType(std::forward<Args>(args)...);
++total_size_;
return iterator(this, total_size_ - 1);
}
/** @brief default constructs n objects at end of
* data structure, n known at compile time */
template<size_t n>
iterator emplace_back_n()
{
size_t bid = buffer_id();
size_t iid = item_id();
if (iid + n < buffer_size) {
RealType *ptr = &(*data_[bid])[iid];
for (size_t i = 0; i < n; ++i) {
new (ptr + i) RealType();
}
total_size_ += n;
return iterator(this, total_size_ - n, total_size_ - n, total_size_);
} else {
size_t allocs_in_curr_buffer = buffer_size - iid;
size_t allocs_in_next_buffer = n - (buffer_size - iid);
RealType *ptr = &(*data_[bid])[iid];
for (size_t i = 0; i < allocs_in_curr_buffer; ++i) {
new (ptr + i) RealType();
}
allocate_buffer();
bid = data_.size() - 1;
iid = 0;
total_size_ += n;
template<size_t n>
iterator emplace_back_n()
{
size_t bid = buffer_id();
size_t iid = item_id();
if (iid + n < buffer_size) {
RealType* ptr = &(*data_[bid])[iid];
for (size_t i = 0; i < n; ++i) {
new (ptr + i) RealType();
}
total_size_ += n;
return iterator(this, total_size_ - n, total_size_ - n, total_size_);
} else {
size_t allocs_in_curr_buffer = buffer_size - iid;
size_t allocs_in_next_buffer = n - (buffer_size - iid);
RealType* ptr = &(*data_[bid])[iid];
for (size_t i = 0; i < allocs_in_curr_buffer; ++i) {
new (ptr + i) RealType();
}
allocate_buffer();
bid = data_.size() - 1;
iid = 0;
total_size_ += n;
RealType *ptr2 = &(*data_[bid])[iid];
for (size_t i = 0; i < allocs_in_next_buffer; i++) {
new (ptr2 + i) RealType();
}
return iterator(this, total_size_ - n, total_size_ - n, total_size_);
}
RealType* ptr2 = &(*data_[bid])[iid];
for (size_t i = 0; i < allocs_in_next_buffer; i++) {
new (ptr2 + i) RealType();
}
return iterator(this, total_size_ - n, total_size_ - n, total_size_);
}
/** @brief default constructs n objects at end of
}
/** @brief default constructs n objects at end of
* data structure, n known at run time
*/
iterator emplace_back_n(size_t n)
{
size_t bid = buffer_id();
size_t iid = item_id();
if (iid + n < buffer_size) {
RealType *ptr = &(*data_[bid])[iid];
for (size_t i = 0; i < n; ++i) {
new (ptr + i) RealType();
}
total_size_ += n;
return iterator(this, total_size_ - n, total_size_ - n, total_size_);
} else {
size_t allocs_in_curr_buffer = buffer_size - iid;
size_t allocs_in_next_buffer = n - (buffer_size - iid);
RealType *ptr = &(*data_[bid])[iid];
for (size_t i = 0; i < allocs_in_curr_buffer; ++i) {
new (ptr + i) RealType();
}
allocate_buffer();
bid = data_.size() - 1;
iid = 0;
total_size_ += n;
iterator emplace_back_n(size_t n)
{
size_t bid = buffer_id();
size_t iid = item_id();
if (iid + n < buffer_size) {
RealType* ptr = &(*data_[bid])[iid];
for (size_t i = 0; i < n; ++i) {
new (ptr + i) RealType();
}
total_size_ += n;
return iterator(this, total_size_ - n, total_size_ - n, total_size_);
} else {
size_t allocs_in_curr_buffer = buffer_size - iid;
size_t allocs_in_next_buffer = n - (buffer_size - iid);
RealType* ptr = &(*data_[bid])[iid];
for (size_t i = 0; i < allocs_in_curr_buffer; ++i) {
new (ptr + i) RealType();
}
allocate_buffer();
bid = data_.size() - 1;
iid = 0;
total_size_ += n;
RealType *ptr2 = &(*data_[bid])[iid];
for (size_t i = 0; i < allocs_in_next_buffer; i++) {
new (ptr2 + i) RealType();
}
return iterator(this, total_size_ - n, total_size_ - n, total_size_);
}
RealType* ptr2 = &(*data_[bid])[iid];
for (size_t i = 0; i < allocs_in_next_buffer; i++) {
new (ptr2 + i) RealType();
}
return iterator(this, total_size_ - n, total_size_ - n, total_size_);
}
}
/** @brief number of elements */
size_t size() const { return total_size_; }
/** @brief number of elements */
size_t size() const { return total_size_; }
/** @brief total capacity */
size_t capacity() const { return data_.size() * buffer_size; }
/** @brief total capacity */
size_t capacity() const { return data_.size() * buffer_size; }
/** @brief iterator helpers */
iterator begin() { return iterator(this, 0); }
iterator end() { return iterator(this, total_size_); }
const_iterator begin() const { return const_iterator(this, 0); }
const_iterator end() const { return const_iterator(this, total_size_); }
/** @brief iterator helpers */
iterator begin() { return iterator(this, 0); }
iterator end() { return iterator(this, total_size_); }
const_iterator begin() const { return const_iterator(this, 0); }
const_iterator end() const { return const_iterator(this, total_size_); }
iterator last_checkpoint() { return iterator(this, checkpoints_.back(), 0, total_size_); }
iterator first_checkpoint() { return iterator(this, checkpoints_[0], 0, total_size_); };
iterator checkpoint_at(size_t index)
{
return iterator(this, checkpoints_[index], 0, total_size_);
};
iterator last_checkpoint()
{
return iterator(this, checkpoints_.back(), 0, total_size_);
}
iterator first_checkpoint()
{
return iterator(this, checkpoints_[0], 0, total_size_);
};
iterator checkpoint_at(size_t index)
{
return iterator(this, checkpoints_[index], 0, total_size_);
};
/** @brief searches for item in allocator
/** @brief searches for item in allocator
* only used to find gradient nodes for propagation */
iterator find(const RealType *const item)
{
return std::find_if(begin(), end(), [&](const RealType &val) { return &val == item; });
}
/** @brief vector like access,
iterator find(const RealType* const item)
{
return std::find_if(
begin(), end(), [&](const RealType& val) { return &val == item; });
}
/** @brief vector like access,
* currently unused anywhere but very useful for debugging
*/
RealType &operator[](std::size_t i)
{
BOOST_MATH_ASSERT(i < total_size_);
return (*data_[i / buffer_size])[i % buffer_size];
}
const RealType &operator[](std::size_t i) const
{
BOOST_MATH_ASSERT(i < total_size_);
return (*data_[i / buffer_size])[i % buffer_size];
}
RealType& operator[](std::size_t i)
{
BOOST_MATH_ASSERT(i < total_size_);
return (*data_[i / buffer_size])[i % buffer_size];
}
const RealType& operator[](std::size_t i) const
{
BOOST_MATH_ASSERT(i < total_size_);
return (*data_[i / buffer_size])[i % buffer_size];
}
};
} // namespace detail
} // namespace reverse_mode

View File

@@ -1368,6 +1368,13 @@ test-suite test_reverse_mode_autodiff
[ run test_reverse_mode_autodiff_basic_math_ops.cpp /boost/test//boost_unit_test_framework : : : <toolset>gcc-mingw:<cxxflags>-Wa,-mbig-obj <debug-symbols>off <toolset>msvc:<cxxflags>/bigobj release ]
[ run test_reverse_mode_autodiff_error_functions.cpp /boost/test//boost_unit_test_framework : : : <toolset>gcc-mingw:<cxxflags>-Wa,-mbig-obj <debug-symbols>off <toolset>msvc:<cxxflags>/bigobj release ]
;
test-suite gradient_based_optimizers
:
[ run test_gradient_descent_optimizer.cpp /boost/test//boost_unit_test_framework : : : <toolset>gcc-mingw:<cxxflags>-Wa,-mbig-obj <debug-symbols>off <toolset>msvc:<cxxflags>/bigobj release ]
[ run test_nesterov_optimizer.cpp /boost/test//boost_unit_test_framework : : : <toolset>gcc-mingw:<cxxflags>-Wa,-mbig-obj <debug-symbols>off <toolset>msvc:<cxxflags>/bigobj release ]
[ run test_lbfgs.cpp /boost/test//boost_unit_test_framework : : : <toolset>gcc-mingw:<cxxflags>-Wa,-mbig-obj <debug-symbols>off <toolset>msvc:<cxxflags>/bigobj release ]
;
# BEGIN AUTODIFF LONG RUNNING TESTS
test-suite autodiff-long-running-tests
:
@@ -2355,6 +2362,6 @@ explicit no_eh_tests ;
# Some aliases which group blocks of tests for CI testing:
alias github_ci_block_1 : special_fun float128_tests distribution_tests mp misc concepts ;
alias github_ci_block_2 : quadrature interpolators autodiff test_reverse_mode_autodiff ../example//examples ../tools ;
alias github_ci_block_2 : quadrature interpolators autodiff test_reverse_mode_autodiff gradient_based_optimizers ../example//examples ../tools ;
explicit github_ci_block_1 ;
explicit github_ci_block_2 ;