The Gaudi Framework  master (181af51f)
Loading...
Searching...
No Matches
Monotonic.h
Go to the documentation of this file.
1/***********************************************************************************\
2* (c) Copyright 2019-2022 CERN for the benefit of the LHCb and ATLAS collaborations *
3* *
4* This software is distributed under the terms of the Apache version 2 licence, *
5* copied verbatim in the file "LICENSE". *
6* *
7* In applying this licence, CERN does not waive the privileges and immunities *
8* granted to it by virtue of its status as an Intergovernmental Organization *
9* or submit itself to any jurisdiction. *
10\***********************************************************************************/
11#pragma once
12
14#include <GaudiKernel/Kernel.h>
15#include <boost/container/small_vector.hpp>
16#include <cstddef>
17#include <gsl/span>
18#include <numeric>
19#include <utility>
20
21namespace Gaudi::Arena {
22 namespace details {
23 template <std::size_t Alignment>
24 constexpr std::size_t align_up( std::size_t n ) {
25 return ( n + ( Alignment - 1 ) ) & ~( Alignment - 1 );
26 }
27 } // namespace details
28
45 template <std::size_t Alignment = alignof( std::max_align_t ), typename UpstreamAllocator = std::allocator<std::byte>>
46 class Monotonic {
47 // Restriction could be lifted, see @todo above.
48 static_assert( std::is_empty_v<UpstreamAllocator>, "Stateful upstream allocators are not yet supported." );
49
51 std::size_t m_next_block_size{};
52
54 std::size_t m_allocations{ 0 };
55
57 std::byte* m_current{ nullptr };
58
60 std::byte* m_current_end{ nullptr };
61
63 boost::container::small_vector<gsl::span<std::byte>, 1> m_all_blocks;
64
66 static constexpr std::size_t growth_factor = 2;
67
68 public:
69 static constexpr std::size_t alignment = Alignment;
70
74 Monotonic( std::size_t next_block_size ) noexcept
75 : m_next_block_size{ details::align_up<Alignment>( next_block_size ) } {}
76
77 ~Monotonic() noexcept {
78 for ( auto block : m_all_blocks ) { UpstreamAllocator{}.deallocate( block.data(), block.size() ); }
79 }
80
81 // Allocators will hold pointers to instances of this class, deleting these
82 // methods makes it harder to accidentally invalidate those pointers...
83 Monotonic( Monotonic&& ) = delete;
84 Monotonic( Monotonic const& ) = delete;
86 Monotonic& operator=( Monotonic const& ) = delete;
87
91 template <std::size_t ReqAlign>
92 std::byte* allocate( std::size_t n ) {
93 // If the requested alignment was larger we would need to round up
94 // m_current -- instead of implementing that, just assert it's not
95 // the case.
96 static_assert( ReqAlign <= alignment, "Requested alignment too large for this Gaudi::Arena::Monotonic!" );
97 // Figure out how many bytes we need to allocate
98 std::size_t const aligned_n = details::align_up<Alignment>( n );
99 // Check that we have a current block and this request fits inside it
100 if ( !m_current || m_current + aligned_n > m_current_end ) {
101 // Calculate our next block size
102 auto next_block_size = std::max( m_next_block_size, aligned_n );
103 // And update the estimate of what comes after that, following a geometric series
105 // Allocate the new block and mark it as the current one
106 m_current = UpstreamAllocator{}.allocate( next_block_size );
107 m_current_end = m_current + next_block_size;
108 // Add it to the list of blocks that we'll eventually deallocate
109 m_all_blocks.emplace_back( m_current, next_block_size );
110 }
112 return std::exchange( m_current, m_current + aligned_n );
113 }
114
117 constexpr void deallocate( std::byte*, std::size_t ) noexcept {}
118
126 void reset() noexcept {
127 m_allocations = 0;
128 if ( !m_all_blocks.empty() ) {
129 // Only re-use the first block, deallocate any others
130 if ( m_all_blocks.size() > 1 ) {
131 for ( std::size_t i = 1; i < m_all_blocks.size(); ++i ) {
132 UpstreamAllocator{}.deallocate( m_all_blocks[i].data(), m_all_blocks[i].size() );
133 }
134 m_all_blocks.resize( 1 );
135 }
136 auto reused_block = m_all_blocks.front();
137 m_current = reused_block.data();
138 m_current_end = m_current + reused_block.size();
140 }
141 }
142
145 [[nodiscard]] std::size_t capacity() const noexcept {
146 return std::accumulate( m_all_blocks.begin(), m_all_blocks.end(), 0ul,
147 []( std::size_t sum, auto block ) { return sum + block.size(); } );
148 }
149
152 [[nodiscard]] std::size_t size() const noexcept { return capacity() - ( m_current_end - m_current ); }
153
156 [[nodiscard]] std::size_t num_blocks() const noexcept { return m_all_blocks.size(); }
157
160 [[nodiscard]] std::size_t num_allocations() const noexcept { return m_allocations; }
161 };
162} // namespace Gaudi::Arena
163
164namespace Gaudi::Allocator {
168 template <typename T, typename DefaultResource = void, std::size_t Alignment = alignof( std::max_align_t ),
169 typename UpstreamAllocator = std::allocator<std::byte>>
172} // namespace Gaudi::Allocator
A fast memory arena that does not track deallocations.
Definition Monotonic.h:46
Monotonic & operator=(Monotonic const &)=delete
boost::container::small_vector< gsl::span< std::byte >, 1 > m_all_blocks
All memory blocks owned by this arena.
Definition Monotonic.h:63
Monotonic(std::size_t next_block_size) noexcept
Construct an arena whose first block have approximately the given size.
Definition Monotonic.h:74
std::byte * m_current_end
One byte past the end of the current block, or nullptr if it doesn't exist.
Definition Monotonic.h:60
std::size_t num_blocks() const noexcept
Query how many blocks of memory this arena owns.
Definition Monotonic.h:156
std::size_t m_next_block_size
Size (in bytes) of the next block to be allocated.
Definition Monotonic.h:51
std::size_t size() const noexcept
Query how much memory was used from this arena, in bytes.
Definition Monotonic.h:152
std::byte * m_current
Current position in the current block, or nullptr if there is no current block.
Definition Monotonic.h:57
Monotonic(Monotonic const &)=delete
void reset() noexcept
Signal that this arena may start re-using the memory resources.
Definition Monotonic.h:126
static constexpr std::size_t alignment
Definition Monotonic.h:69
constexpr void deallocate(std::byte *, std::size_t) noexcept
Deallocations are not tracked, so this is a no-op!
Definition Monotonic.h:117
static constexpr std::size_t growth_factor
Approximate factor by which each block is larger than its predecessor.
Definition Monotonic.h:66
std::size_t num_allocations() const noexcept
Query how many allocations this arena has served.
Definition Monotonic.h:160
std::size_t capacity() const noexcept
Query how much memory is owned by this arena, in bytes.
Definition Monotonic.h:145
std::size_t m_allocations
Number of allocation requests served by this arena.
Definition Monotonic.h:54
Monotonic(Monotonic &&)=delete
std::byte * allocate(std::size_t n)
Return an aligned point to n bytes of memory.
Definition Monotonic.h:92
Monotonic & operator=(Monotonic &&)=delete
::Gaudi::Allocator::Arena<::Gaudi::Arena::Monotonic< Alignment, UpstreamAllocator >, T, DefaultResource > MonotonicArena
Definition Monotonic.h:170
constexpr std::size_t align_up(std::size_t n)
Definition Monotonic.h:24
Custom allocator holding a pointer to a generic memory resource.
Definition Arena.h:29