The Gaudi Framework  master (37c0b60a)
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 
13 #include <Gaudi/Allocator/Arena.h>
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 
21 namespace Gaudi::Arena {
22  namespace details {
23  template <std::size_t Alignment>
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 
52 
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;
85  Monotonic& operator=( Monotonic&& ) = 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
104  m_next_block_size = details::align_up<Alignment>( growth_factor * next_block_size );
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  }
111  m_allocations++;
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();
139  m_next_block_size = details::align_up<Alignment>( growth_factor * 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 
164 namespace 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
Gaudi::Arena::Monotonic::capacity
std::size_t capacity() const noexcept
Query how much memory is owned by this arena, in bytes.
Definition: Monotonic.h:145
Gaudi::Arena::Monotonic::allocate
std::byte * allocate(std::size_t n)
Return an aligned point to n bytes of memory.
Definition: Monotonic.h:92
Gaudi::Arena::Monotonic::operator=
Monotonic & operator=(Monotonic &&)=delete
Gaudi::Arena::Monotonic::Monotonic
Monotonic(Monotonic const &)=delete
Gaudi::Arena::Monotonic::m_current_end
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
Gaudi::Arena::Monotonic::m_next_block_size
std::size_t m_next_block_size
Size (in bytes) of the next block to be allocated.
Definition: Monotonic.h:51
Gaudi::Arena::Monotonic::growth_factor
static constexpr std::size_t growth_factor
Approximate factor by which each block is larger than its predecessor.
Definition: Monotonic.h:66
Gaudi::Arena::Monotonic::reset
void reset() noexcept
Signal that this arena may start re-using the memory resources.
Definition: Monotonic.h:126
details
Definition: AnyDataWrapper.h:19
Arena.h
Gaudi::Arena::Monotonic::Monotonic
Monotonic(std::size_t next_block_size) noexcept
Construct an arena whose first block have approximately the given size.
Definition: Monotonic.h:74
Gaudi::Arena::Monotonic::~Monotonic
~Monotonic() noexcept
Definition: Monotonic.h:77
Gaudi::Arena::details::align_up
constexpr std::size_t align_up(std::size_t n)
Definition: Monotonic.h:24
Gaudi::Arena::Monotonic::operator=
Monotonic & operator=(Monotonic const &)=delete
std::accumulate
T accumulate(T... args)
Gaudi::Arena::Monotonic::m_all_blocks
boost::container::small_vector< gsl::span< std::byte >, 1 > m_all_blocks
All memory blocks owned by this arena.
Definition: Monotonic.h:63
Gaudi::Arena::Monotonic
A fast memory arena that does not track deallocations.
Definition: Monotonic.h:46
cpluginsvc.n
n
Definition: cpluginsvc.py:234
Gaudi::Arena::Monotonic::deallocate
constexpr void deallocate(std::byte *, std::size_t) noexcept
Deallocations are not tracked, so this is a no-op!
Definition: Monotonic.h:117
Gaudi::Allocator::Arena
Custom allocator holding a pointer to a generic memory resource.
Definition: Arena.h:29
Kernel.h
Gaudi::Arena::Monotonic::size
std::size_t size() const noexcept
Query how much memory was used from this arena, in bytes.
Definition: Monotonic.h:152
Gaudi::Arena::Monotonic::Monotonic
Monotonic(Monotonic &&)=delete
std::allocator
STL class.
std::size_t
Gaudi::Arena::Monotonic::alignment
static constexpr std::size_t alignment
Definition: Monotonic.h:69
Gaudi::Arena::Monotonic::num_allocations
std::size_t num_allocations() const noexcept
Query how many allocations this arena has served.
Definition: Monotonic.h:160
Gaudi::Arena::Monotonic::m_allocations
std::size_t m_allocations
Number of allocation requests served by this arena.
Definition: Monotonic.h:54
std::max
T max(T... args)
std::max_align_t
Gaudi::Arena::Monotonic::m_current
std::byte * m_current
Current position in the current block, or nullptr if there is no current block.
Definition: Monotonic.h:57
Gaudi::Arena::Monotonic::num_blocks
std::size_t num_blocks() const noexcept
Query how many blocks of memory this arena owns.
Definition: Monotonic.h:156
Gaudi::Arena
Definition: Monotonic.h:21
Gaudi::Allocator
Definition: Arena.h:15