mirror of https://github.com/CGAL/cgal
202 lines
6.4 KiB
C++
202 lines
6.4 KiB
C++
// Copyright (c) 2025 GeometryFactory Sarl (France).
|
|
//
|
|
// This file is part of CGAL (www.cgal.org).
|
|
//
|
|
// $URL$
|
|
// $Id$
|
|
// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial
|
|
//
|
|
// Author(s) : Laurent Rineau
|
|
|
|
#ifndef CGAL_RANDOM_ALLOCATOR_H
|
|
#define CGAL_RANDOM_ALLOCATOR_H
|
|
|
|
#include <boost/container/static_vector.hpp>
|
|
#include <boost/dynamic_bitset.hpp>
|
|
#include <memory>
|
|
#include <vector>
|
|
#include <random>
|
|
|
|
#if CGAL_DEBUG_RANDOM_ALLOCATOR
|
|
# if __has_include(<format>)
|
|
# include <iostream>
|
|
# include <format>
|
|
# if __cpp_lib_format >= 201907L
|
|
# define CGAL_DEBUG_RANDOM_ALLOCATOR_OKAY 1
|
|
# endif
|
|
# endif
|
|
# if ! CGAL_DEBUG_RANDOM_ALLOCATOR_OKAY
|
|
# error "CGAL_DEBUG_RANDOM_ALLOCATOR requires <format> and C++20 std::format"
|
|
# endif
|
|
#endif
|
|
|
|
namespace CGAL {
|
|
|
|
// A random allocator that allocates blocks of memory and returns random
|
|
// locations. To use only for debugging purposes. That allocator is:
|
|
// - not efficient,
|
|
// - not thread-safe,
|
|
// - and increases memory-fragmentation and non-locality.
|
|
template <typename T, typename Upstream_allocator = std::allocator<T>> class Random_allocator
|
|
{
|
|
constexpr static std::size_t minimal_block_size = 1024;
|
|
constexpr static std::size_t random_size = 64;
|
|
|
|
struct Blocks
|
|
{
|
|
struct Block
|
|
{
|
|
T* data = nullptr;
|
|
boost::dynamic_bitset<> available;
|
|
std::size_t maximal_continuous_free_space = 0;
|
|
|
|
auto size() const { return available.size(); }
|
|
};
|
|
|
|
Upstream_allocator alloc;
|
|
std::vector<Block> blocks; // List of allocated blocks
|
|
|
|
void allocate_new_block(std::size_t block_size = minimal_block_size)
|
|
{
|
|
auto& block = blocks.emplace_back();
|
|
block.data = alloc.allocate(block_size);
|
|
block.available.resize(block_size, true);
|
|
block.maximal_continuous_free_space = block_size;
|
|
}
|
|
|
|
Blocks(const Upstream_allocator& alloc) : alloc(alloc) { allocate_new_block(); }
|
|
|
|
~Blocks()
|
|
{
|
|
for(auto& block : blocks) {
|
|
alloc.deallocate(block.data, block.size());
|
|
}
|
|
}
|
|
};
|
|
using Block = typename Blocks::Block;
|
|
using Ptr = std::shared_ptr<Blocks>;
|
|
|
|
Ptr ptr_;
|
|
std::mt19937 gen;
|
|
|
|
public:
|
|
using value_type = T;
|
|
using size_type = std::size_t;
|
|
using difference_type = std::ptrdiff_t;
|
|
using pointer = T*;
|
|
using const_pointer = const T*;
|
|
using reference = T&;
|
|
using const_reference = const T&;
|
|
using allocator_type = Upstream_allocator;
|
|
|
|
Random_allocator(const Upstream_allocator& alloc = {}) noexcept;
|
|
|
|
pointer allocate(size_type n, const void* hint = 0);
|
|
void deallocate(pointer p, size_type n);
|
|
|
|
template <typename U, typename... Args> void construct(U* p, Args&&... args);
|
|
template <typename U> void destroy(U* p);
|
|
|
|
size_type max_size() const noexcept;
|
|
|
|
template <typename U> struct rebind
|
|
{
|
|
using upstream_traits = std::allocator_traits<Upstream_allocator>;
|
|
using other_upstream_allocator = typename upstream_traits::template rebind_alloc<U>;
|
|
using other = Random_allocator<U, other_upstream_allocator>;
|
|
};
|
|
|
|
bool operator==(const Random_allocator&) const noexcept;
|
|
bool operator!=(const Random_allocator&) const noexcept;
|
|
};
|
|
|
|
// Implementation of Random_allocator methods
|
|
template <typename T, typename Upstream_allocator>
|
|
Random_allocator<T, Upstream_allocator>::Random_allocator(const Upstream_allocator& alloc) noexcept
|
|
: ptr_(std::make_shared<Blocks>(alloc)), gen(std::random_device()())
|
|
{
|
|
}
|
|
|
|
template <typename T, typename Upstream_allocator>
|
|
typename Random_allocator<T, Upstream_allocator>::pointer
|
|
Random_allocator<T, Upstream_allocator>::allocate(size_type n, const void* hint)
|
|
{
|
|
boost::container::static_vector<std::pair<Block*, size_type>, random_size> found_spaces;
|
|
for(auto it = ptr_->blocks.rbegin(), end = ptr_->blocks.rend(); it != end; ++it) {
|
|
auto& block = *it;
|
|
if(block.maximal_continuous_free_space < n)
|
|
continue;
|
|
auto& available = block.available;
|
|
const auto block_size = block.size();
|
|
size_type found_max_free_space = 0;
|
|
auto index = available.find_first();
|
|
while(index != boost::dynamic_bitset<>::npos) {
|
|
available.flip();
|
|
const auto end_of_free_block = (std::min)(available.find_next(index), block_size);
|
|
available.flip();
|
|
auto free_space = end_of_free_block - index;
|
|
found_max_free_space = (std::max)(found_max_free_space, free_space);
|
|
while(free_space > n && found_spaces.size() < found_spaces.capacity()) {
|
|
found_spaces.emplace_back(std::addressof(block), index);
|
|
free_space -= n;
|
|
index += n;
|
|
}
|
|
index = block.available.find_next(end_of_free_block);
|
|
}
|
|
block.maximal_continuous_free_space = found_max_free_space;
|
|
if(found_spaces.size() == found_spaces.capacity())
|
|
break;
|
|
}
|
|
if(!found_spaces.empty()) {
|
|
std::uniform_int_distribution<> dis(0, found_spaces.size() - 1);
|
|
auto i = dis(gen);
|
|
auto [block, index] = found_spaces[i];
|
|
block->available.set(index, n, false);
|
|
#if CGAL_DEBUG_RANDOM_ALLOCATOR
|
|
std::clog << std::format("CGAL::Random_allocator debug info: n = {}, found_spaces.size() = {}, i = {},"
|
|
" block id = {}, block size = {}, index = {}\n",
|
|
n, found_spaces.size(), i, block - ptr_->blocks.data(), block->size(), index);
|
|
#endif // CGAL_DEBUG_RANDOM_ALLOCATOR
|
|
return block->data + index;
|
|
}
|
|
size_type block_size = (std::max)(n * random_size, minimal_block_size);
|
|
ptr_->allocate_new_block(block_size);
|
|
return allocate(n, hint);
|
|
}
|
|
|
|
template <typename T, typename Upstream_allocator>
|
|
void Random_allocator<T, Upstream_allocator>::deallocate(pointer p, size_type n)
|
|
{
|
|
for(auto& block : ptr_->blocks) {
|
|
if(block.data <= p && p < block.data + block.size()) {
|
|
block.available.set(p - block.data, n, true);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
template <typename T, typename Upstream_allocator>
|
|
template <typename U, typename... Args>
|
|
void Random_allocator<T, Upstream_allocator>::construct(U* p, Args&&... args)
|
|
{
|
|
::new((void*)p) U(std::forward<Args>(args)...);
|
|
}
|
|
|
|
template <typename T, typename Upstream_allocator>
|
|
template <typename U>
|
|
void Random_allocator<T, Upstream_allocator>::destroy(U* p)
|
|
{
|
|
p->~U();
|
|
}
|
|
|
|
template <typename T, typename Upstream_allocator>
|
|
typename Random_allocator<T, Upstream_allocator>::size_type
|
|
Random_allocator<T, Upstream_allocator>::max_size() const noexcept
|
|
{
|
|
return (std::numeric_limits<size_type>::max)() / sizeof(T);
|
|
}
|
|
|
|
} // namespace CGAL
|
|
|
|
#endif // CGAL_RANDOM_ALLOCATOR_H
|