Automatic Differentiation
 
Loading...
Searching...
No Matches
stack_alloc.hpp
Go to the documentation of this file.
1#ifndef STAN_MATH_MEMORY_STACK_ALLOC_HPP
2#define STAN_MATH_MEMORY_STACK_ALLOC_HPP
3
4// TODO(Bob): <cstddef> replaces this ifdef in C++11, until then this
5// is best we can do to get safe pointer casts to uints.
6#include <stdint.h>
8#include <cstdlib>
9#include <cstddef>
10#include <sstream>
11#include <stdexcept>
12#include <vector>
13
14namespace stan {
15namespace math {
16
28template <typename T>
29bool is_aligned(T* ptr, unsigned int bytes_aligned) {
30 return (reinterpret_cast<uintptr_t>(ptr) % bytes_aligned) == 0U;
31}
32
33namespace internal {
34const size_t DEFAULT_INITIAL_NBYTES = 1 << 16; // 64KB
35
36// FIXME: enforce alignment
37// big fun to inline, but only called twice
38inline char* eight_byte_aligned_malloc(size_t size) {
39 char* ptr = static_cast<char*>(malloc(size));
40 if (!ptr) {
41 return ptr; // malloc failed to alloc
42 }
43 if (!is_aligned(ptr, 8U)) {
44 std::stringstream s;
45 s << "invalid alignment to 8 bytes, ptr="
46 << reinterpret_cast<uintptr_t>(ptr) << std::endl;
47 throw std::runtime_error(s.str());
48 }
49 return ptr;
50}
51} // namespace internal
52
73 private:
74 std::vector<char*> blocks_; // storage for blocks,
75 // may be bigger than cur_block_
76 std::vector<size_t> sizes_; // could store initial & shift for others
77 size_t cur_block_; // index into blocks_ for next alloc
78 char* cur_block_end_; // ptr to cur_block_ptr_ + sizes_[cur_block_]
79 char* next_loc_; // ptr to next available spot in cur
80 // block
81 // next three for keeping track of nested allocations on top of stack:
82 std::vector<size_t> nested_cur_blocks_;
83 std::vector<char*> nested_next_locs_;
84 std::vector<char*> nested_cur_block_ends_;
85
94 char* move_to_next_block(size_t len) {
95 char* result;
96 ++cur_block_;
97 // Find the next block (if any) containing at least len bytes.
98 while ((cur_block_ < blocks_.size()) && (sizes_[cur_block_] < len)) {
99 ++cur_block_;
100 }
101 // Allocate a new block if necessary.
102 if (unlikely(cur_block_ >= blocks_.size())) {
103 // New block should be max(2*size of last block, len) bytes.
104 size_t newsize = sizes_.back() * 2;
105 if (newsize < len) {
106 newsize = len;
107 }
109 if (!blocks_.back()) {
110 throw std::bad_alloc();
111 }
112 sizes_.push_back(newsize);
113 }
114 result = blocks_[cur_block_];
115 // Get the object's state back in order.
116 next_loc_ = result + len;
117 cur_block_end_ = result + sizes_[cur_block_];
118 return result;
119 }
120
121 public:
131 explicit stack_alloc(size_t initial_nbytes = internal::DEFAULT_INITIAL_NBYTES)
132 : blocks_(1, internal::eight_byte_aligned_malloc(initial_nbytes)),
133 sizes_(1, initial_nbytes),
134 cur_block_(0),
135 cur_block_end_(blocks_[0] + initial_nbytes),
136 next_loc_(blocks_[0]) {
137 if (!blocks_[0]) {
138 throw std::bad_alloc(); // no msg allowed in bad_alloc ctor
139 }
140 }
141
149 // free ALL blocks
150 for (auto& block : blocks_) {
151 if (block) {
152 free(block);
153 }
154 }
155 }
156
171 inline void* alloc(size_t len) {
172 size_t pad = len % 8 == 0 ? 0 : 8 - len % 8;
173
174 // Typically, just return and increment the next location.
175 char* result = next_loc_;
176 next_loc_ += len + pad;
177 // Occasionally, we have to switch blocks.
179 result = move_to_next_block(len);
180 }
181 return reinterpret_cast<void*>(result);
182 }
183
192 template <typename T>
193 inline T* alloc_array(size_t n) {
194 return static_cast<T*>(alloc(n * sizeof(T)));
195 }
196
203 inline void recover_all() {
204 cur_block_ = 0;
205 next_loc_ = blocks_[0];
207 }
208
213 inline void start_nested() {
215 nested_next_locs_.push_back(next_loc_);
217 }
218
222 inline void recover_nested() {
223 if (unlikely(nested_cur_blocks_.empty())) {
224 recover_all();
225 }
226
228 nested_cur_blocks_.pop_back();
229
231 nested_next_locs_.pop_back();
232
234 nested_cur_block_ends_.pop_back();
235 }
236
242 inline void free_all() {
243 // frees all BUT the first (index 0) block
244 for (size_t i = 1; i < blocks_.size(); ++i) {
245 if (blocks_[i]) {
246 free(blocks_[i]);
247 }
248 }
249 sizes_.resize(1);
250 blocks_.resize(1);
251 recover_all();
252 }
253
264 inline size_t bytes_allocated() const {
265 size_t sum = 0;
266 for (size_t i = 0; i <= cur_block_; ++i) {
267 sum += sizes_[i];
268 }
269 return sum;
270 }
271
280 inline bool in_stack(const void* ptr) const {
281 for (size_t i = 0; i < cur_block_; ++i) {
282 if (ptr >= blocks_[i] && ptr < blocks_[i] + sizes_[i]) {
283 return true;
284 }
285 }
286 if (ptr >= blocks_[cur_block_] && ptr < next_loc_) {
287 return true;
288 }
289 return false;
290 }
291};
292
293} // namespace math
294} // namespace stan
295#endif
char * move_to_next_block(size_t len)
Moves us to the next block of memory, allocating that block if necessary, and allocates len bytes of ...
bool in_stack(const void *ptr) const
Indicates whether the memory in the pointer is in the stack.
T * alloc_array(size_t n)
Allocate an array on the arena of the specified size to hold values of the specified template paramet...
std::vector< char * > blocks_
stack_alloc(size_t initial_nbytes=internal::DEFAULT_INITIAL_NBYTES)
Construct a resizable stack allocator initially holding the specified number of bytes.
std::vector< size_t > sizes_
~stack_alloc()
Destroy this memory allocator.
std::vector< char * > nested_cur_block_ends_
void recover_nested()
recover memory back to the last start_nested call.
std::vector< size_t > nested_cur_blocks_
void * alloc(size_t len)
Return a newly allocated block of memory of the appropriate size managed by the stack allocator.
void start_nested()
Store current positions before doing nested operation so can recover back to start.
size_t bytes_allocated() const
Return number of bytes allocated to this instance by the heap.
std::vector< char * > nested_next_locs_
void recover_all()
Recover all the memory used by the stack allocator.
void free_all()
Free all memory used by the stack allocator other than the initial block allocation back to the syste...
An instance of this class provides a memory pool through which blocks of raw memory may be allocated ...
#define unlikely(x)
int64_t size(const T &m)
Returns the size (number of the elements) of a matrix_cl or var_value<matrix_cl<T>>.
Definition size.hpp:19
const size_t DEFAULT_INITIAL_NBYTES
char * eight_byte_aligned_malloc(size_t size)
auto block(T_x &&x, size_t i, size_t j, size_t nrows, size_t ncols)
Return a nrows x ncols submatrix starting at (i-1, j-1).
Definition block.hpp:24
bool is_aligned(T *ptr, unsigned int bytes_aligned)
Return true if the specified pointer is aligned on the number of bytes.
auto sum(const std::vector< T > &m)
Return the sum of the entries of the specified standard vector.
Definition sum.hpp:23
The lgamma implementation in stan-math is based on either the reentrant safe lgamma_r implementation ...