Stan Math Library  2.8.0
reverse mode automatic differentiation
 All Classes Namespaces Files Functions Variables Typedefs Enumerator Friends Macros Groups
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 #if defined(_MSC_VER)
7  #include <msinttypes.h> // Microsoft Visual Studio lacks full stdint.h
8 #else
9  #include <stdint.h>
10 #endif
12 #include <cstdlib>
13 #include <cstddef>
14 #include <sstream>
15 #include <stdexcept>
16 #include <vector>
17 
18 namespace stan {
19 
20  namespace math {
21 
33  template <typename T>
34  bool is_aligned(T* ptr, unsigned int bytes_aligned) {
35  return (reinterpret_cast<uintptr_t>(ptr) % bytes_aligned) == 0U;
36  }
37 
38 
39  namespace {
40  const size_t DEFAULT_INITIAL_NBYTES = 1 << 16; // 64KB
41 
42 
43  // FIXME: enforce alignment
44  // big fun to inline, but only called twice
45  inline char* eight_byte_aligned_malloc(size_t size) {
46  char* ptr = static_cast<char*>(malloc(size));
47  if (!ptr) return ptr; // malloc failed to alloc
48  if (!is_aligned(ptr, 8U)) {
49  std::stringstream s;
50  s << "invalid alignment to 8 bytes, ptr="
51  << reinterpret_cast<uintptr_t>(ptr)
52  << std::endl;
53  throw std::runtime_error(s.str());
54  }
55  return ptr;
56  }
57  }
58 
78  class stack_alloc {
79  private:
80  std::vector<char*> blocks_; // storage for blocks,
81  // may be bigger than cur_block_
82  std::vector<size_t> sizes_; // could store initial & shift for others
83  size_t cur_block_; // index into blocks_ for next alloc
84  char* cur_block_end_; // ptr to cur_block_ptr_ + sizes_[cur_block_]
85  char* next_loc_; // ptr to next available spot in cur
86  // block
87  // next three for keeping track of nested allocations on top of stack:
88  std::vector<size_t> nested_cur_blocks_;
89  std::vector<char*> nested_next_locs_;
90  std::vector<char*> nested_cur_block_ends_;
91 
92 
101  char* move_to_next_block(size_t len) {
102  char* result;
103  ++cur_block_;
104  // Find the next block (if any) containing at least len bytes.
105  while ((cur_block_ < blocks_.size()) && (sizes_[cur_block_] < len))
106  ++cur_block_;
107  // Allocate a new block if necessary.
108  if (unlikely(cur_block_ >= blocks_.size())) {
109  // New block should be max(2*size of last block, len) bytes.
110  size_t newsize = sizes_.back() * 2;
111  if (newsize < len)
112  newsize = len;
113  blocks_.push_back(eight_byte_aligned_malloc(newsize));
114  if (!blocks_.back())
115  throw std::bad_alloc();
116  sizes_.push_back(newsize);
117  }
118  result = blocks_[cur_block_];
119  // Get the object's state back in order.
120  next_loc_ = result + len;
121  cur_block_end_ = result + sizes_[cur_block_];
122  return result;
123  }
124 
125  public:
135  explicit stack_alloc(size_t initial_nbytes = DEFAULT_INITIAL_NBYTES) :
136  blocks_(1, eight_byte_aligned_malloc(initial_nbytes)),
137  sizes_(1, initial_nbytes),
138  cur_block_(0),
139  cur_block_end_(blocks_[0] + initial_nbytes),
140  next_loc_(blocks_[0]) {
141  if (!blocks_[0])
142  throw std::bad_alloc(); // no msg allowed in bad_alloc ctor
143  }
144 
152  // free ALL blocks
153  for (size_t i = 0; i < blocks_.size(); ++i)
154  if (blocks_[i])
155  free(blocks_[i]);
156  }
157 
170  inline void* alloc(size_t len) {
171  // Typically, just return and increment the next location.
172  char* result = next_loc_;
173  next_loc_ += len;
174  // Occasionally, we have to switch blocks.
175  if (unlikely(next_loc_ >= cur_block_end_))
176  result = move_to_next_block(len);
177  return reinterpret_cast<void*>(result);
178  }
179 
188  template <typename T>
189  inline
190  T* alloc_array(size_t n) {
191  return static_cast<T*>(alloc(n * sizeof(T)));
192  }
193 
194 
201  inline void recover_all() {
202  cur_block_ = 0;
203  next_loc_ = blocks_[0];
204  cur_block_end_ = next_loc_ + sizes_[0];
205  }
206 
211  inline void start_nested() {
212  nested_cur_blocks_.push_back(cur_block_);
213  nested_next_locs_.push_back(next_loc_);
214  nested_cur_block_ends_.push_back(cur_block_end_);
215  }
216 
220  inline void recover_nested() {
221  if (unlikely(nested_cur_blocks_.empty()))
222  recover_all();
223 
224  cur_block_ = nested_cur_blocks_.back();
225  nested_cur_blocks_.pop_back();
226 
227  next_loc_ = nested_next_locs_.back();
228  nested_next_locs_.pop_back();
229 
230  cur_block_end_ = nested_cur_block_ends_.back();
231  nested_cur_block_ends_.pop_back();
232  }
233 
239  inline void free_all() {
240  // frees all BUT the first (index 0) block
241  for (size_t i = 1; i < blocks_.size(); ++i)
242  if (blocks_[i])
243  free(blocks_[i]);
244  sizes_.resize(1);
245  blocks_.resize(1);
246  recover_all();
247  }
248 
259  size_t bytes_allocated() {
260  size_t sum = 0;
261  for (size_t i = 0; i <= cur_block_; ++i) {
262  sum += sizes_[i];
263  }
264  return sum;
265  }
266  };
267 
268  }
269 }
270 #endif
fvar< T > sum(const std::vector< fvar< T > > &m)
Return the sum of the entries of the specified standard vector.
Definition: sum.hpp:20
~stack_alloc()
Destroy this memory allocator.
void recover_nested()
recover memory back to the last start_nested call.
void free_all()
Free all memory used by the stack allocator other than the initial block allocation back to the syste...
void recover_all()
Recover all the memory used by the stack allocator.
#define unlikely(x)
Definition: likely.hpp:9
size_t bytes_allocated()
Return number of bytes allocated to this instance by the heap.
T * alloc_array(size_t n)
Allocate an array on the arena of the specified size to hold values of the specified template paramet...
int size(const std::vector< T > &x)
Return the size of the specified standard vector.
Definition: size.hpp:17
void start_nested()
Store current positions before doing nested operation so can recover back to start.
bool is_aligned(T *ptr, unsigned int bytes_aligned)
Return true if the specified pointer is aligned on the number of bytes.
Definition: stack_alloc.hpp:34
stack_alloc(size_t initial_nbytes=DEFAULT_INITIAL_NBYTES)
Construct a resizable stack allocator initially holding the specified number of bytes.
An instance of this class provides a memory pool through which blocks of raw memory may be allocated ...
Definition: stack_alloc.hpp:78
void * alloc(size_t len)
Return a newly allocated block of memory of the appropriate size managed by the stack allocator...

     [ Stan Home Page ] © 2011–2015, Stan Development Team.