0.9.8.10
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
PageArena.h
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2007-2015 Hypertable, Inc.
3  *
4  * This file is part of Hypertable.
5  *
6  * Hypertable is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 3
9  * of the License, or any later version.
10  *
11  * Hypertable is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with Hypertable. If not, see <http://www.gnu.org/licenses/>
18  */
19 
28 #ifndef HYPERTABLE_PAGEARENA_H
29 #define HYPERTABLE_PAGEARENA_H
30 
31 #include <cstdlib>
32 #include <iostream>
33 #include <cstring>
34 #include <cstddef>
35 #include <cassert>
36 #include <algorithm>
37 #include <set>
38 #include <string>
39 
40 #include <boost/noncopyable.hpp>
41 #include <boost/static_assert.hpp>
42 
43 #include "Common/Logger.h"
44 
45 namespace Hypertable {
46 
54  void *allocate(size_t sz) { return std::malloc(sz); }
55 
57  void deallocate(void *p) { if (p) std::free(p); }
58 
60  void freed(size_t sz) { }
61 };
62 
68 template <typename CharT = char, class PageAllocatorT = DefaultPageAllocator>
69 class PageArena : boost::noncopyable {
70  private:
72  enum {
74  };
75 
77 
81  struct Page {
83  char *alloc_end;
84  const char *page_end;
85  char buf[1];
86 
87  Page(const char *end) : next_page(0), page_end(end) {
88  alloc_end = buf;
89  }
90 
91  size_t remain() const {
92  return page_end - alloc_end;
93  }
94 
95  CharT *alloc(size_t sz) {
96  assert(sz <= remain());
97  char *start = alloc_end;
98  alloc_end += sz;
99  return (CharT *)start;
100  }
101  };
102 
103  private: // data
104  Page *m_cur_page;
105  size_t m_used;
106  size_t m_page_limit;
107  size_t m_page_size;
108  size_t m_pages;
109  size_t m_total;
110  PageAllocatorT m_page_allocator;
111 
113  struct LtPageRemain {
114  bool operator()(const Page* p1, const Page*p2) const {
115  return p1->remain() < p2->remain();
116  }
117  };
118 
120  typedef std::set<Page*, LtPageRemain> GappyPages;
121  GappyPages m_gappy_pages;
122 
124 
128  struct TinyBuffer {
129  enum { SIZE = 128 };
130  CharT base[SIZE];
131  size_t fill;
132  inline TinyBuffer() : fill(0) { }
133  inline CharT *alloc(size_t sz) {
134  CharT *p = 0;
135  if (fill + sz <= SIZE) {
136  p = base + fill;
137  fill += sz;
138  }
139  return p;
140  }
141  } m_tinybuf;
142 
143  private:
145  Page *alloc_page(size_t sz, bool prepend = true) {
146  Page *page = (Page *) m_page_allocator.allocate(sz);
148  new (page) Page((char *)page + sz);
149 
150  if (HT_LIKELY(prepend))
151  page->next_page = m_cur_page;
152  else if (m_cur_page) {
153  // insert after cur page, for big/full pages
154  page->next_page = m_cur_page->next_page;
155  m_cur_page->next_page = page;
156  }
157  else // don't leak it
158  m_cur_page = page;
159 
160  ++m_pages;
161  m_total += sz;
162 
163  return page;
164  }
165 
166  inline void ensure_cur_page() {
167  if (HT_UNLIKELY(!m_cur_page)) {
168  m_cur_page = alloc_page(m_page_size);
169  m_page_limit = m_cur_page->remain();
170  }
171  }
172 
173  inline bool is_normal_overflow(size_t sz) {
174  return sz <= m_page_limit && m_cur_page->remain() < m_page_limit / 2;
175  }
176 
177  CharT *alloc_big(size_t sz) {
178  // big enough object to have their own page
179  Page *p = alloc_page(sz + sizeof(Page), false);
180  return p->alloc(sz);
181  }
182 
183  public:
190  const PageAllocatorT &alloc = PageAllocatorT())
191  : m_cur_page(0), m_used(0), m_page_limit(0), m_page_size(page_size),
192  m_pages(0), m_total(0), m_page_allocator(alloc), m_gappy_limit(0) {
193  BOOST_STATIC_ASSERT(sizeof(CharT) == 1);
194  HT_ASSERT(page_size > sizeof(Page));
195  }
196 
201  free();
202  }
203 
205  size_t page_size() {
206  return m_page_size;
207  }
208 
210  void set_page_size(size_t sz) {
211  HT_ASSERT(sz > sizeof(Page));
212  m_page_size = sz;
213  }
214 
216  CharT *alloc(size_t sz) {
217  CharT *tiny;
218  if ((tiny = m_tinybuf.alloc(sz)))
219  return tiny;
220  m_used += sz;
221  ensure_cur_page();
222 
223  if (m_gappy_limit >= sz) {
224  Page f((const char*)sz);
225  f.alloc_end = 0;
226  typename GappyPages::iterator it = m_gappy_pages.lower_bound(&f);
227  Page* page = *it;
228  CharT *p = page->alloc(sz);
229  m_gappy_pages.erase(it);
230  if (page->remain() >= TinyBuffer::SIZE) {
231  m_gappy_pages.insert(page);
232  }
233  m_gappy_limit = m_gappy_pages.size()
234  ? (*m_gappy_pages.rbegin())->remain()
235  : 0;
236  return p;
237  }
238 
239  // common case
240  if (HT_LIKELY(sz <= m_cur_page->remain()))
241  return m_cur_page->alloc(sz);
242 
243  if (is_normal_overflow(sz)) {
244  if (m_cur_page->remain() >= TinyBuffer::SIZE) {
245  m_gappy_pages.insert(m_cur_page);
246  m_gappy_limit = (*m_gappy_pages.rbegin())->remain();
247  }
248  m_cur_page = alloc_page(m_page_size);
249  return m_cur_page->alloc(sz);
250  }
251  return alloc_big(sz);
252  }
253 
255  CharT *realloc(void *p, size_t oldsz, size_t newsz) {
256  HT_ASSERT(m_cur_page);
257  // if we're the last one on the current page with enough space
258  if ((char *)p + oldsz == m_cur_page->alloc_end
259  && (char *)p + newsz < m_cur_page->page_end) {
260  m_cur_page->alloc_end = (char *)p + newsz;
261  return (CharT *)p;
262  }
263  CharT *copy = alloc(newsz);
264  memcpy(copy, p, newsz > oldsz ? oldsz : newsz);
265  return copy;
266  }
267 
274  CharT *dup(const CharT *s) {
275  if (HT_UNLIKELY(!s))
276  return NULL;
277 
278  size_t len = std::strlen(s) + 1;
279  CharT *copy = alloc(len);
280  memcpy(copy, s, len);
281  return copy;
282  }
283 
289  CharT *dup(const std::string& s) {
290  return dup(s.c_str(), s.length() + 1);
291  }
292 
299  CharT *dup(const void *s, size_t len) {
300  if (HT_UNLIKELY(!s))
301  return NULL;
302 
303  CharT *copy = alloc(len);
304  memcpy(copy, s, len);
305  return copy;
306  }
307 
312  void free() {
313  Page *page;
314 
315  while (m_cur_page) {
316  page = m_cur_page;
317  m_cur_page = m_cur_page->next_page;
318  m_page_allocator.deallocate(page);
319  }
320  m_page_allocator.freed(m_total);
321  m_pages = m_total = m_used = 0;
322 
323  m_tinybuf = TinyBuffer();
324  m_gappy_pages.clear();
325  m_gappy_limit = 0;
326  }
327 
329  void swap(Self &other) {
330  std::swap(m_cur_page, other.m_cur_page);
331  std::swap(m_page_limit, other.m_page_limit);
332  std::swap(m_page_size, other.m_page_size);
333  std::swap(m_pages, other.m_pages);
334  std::swap(m_total, other.m_total);
335  std::swap(m_used, other.m_used);
336  std::swap(m_tinybuf, other.m_tinybuf);
337  std::swap(m_gappy_pages, other.m_gappy_pages);
338  std::swap(m_gappy_limit, other.m_gappy_limit);
339  }
340 
344  std::ostream& dump_stat(std::ostream& out) const {
345  out << "pages=" << m_pages << ", total=" << m_total << ", used=" << m_used
346  << "(" << m_used * 100. / m_total << "%)";
347  return out;
348  }
349 
351  size_t used() const { return m_used + m_tinybuf.fill; }
352 
354  size_t pages() const { return m_pages; }
355 
357  size_t total() const { return m_total + TinyBuffer::SIZE; }
358 };
359 
362 
363 template <typename CharT, class PageAlloc>
364 inline std::ostream&
365 operator<<(std::ostream& out, const PageArena<CharT, PageAlloc> &m) {
366  return m.dump_stat(out);
367 }
368 
371 } // namespace Hypertable
372 
373 #endif // !HYPERTABLE_PAGEARENA_H
void deallocate(void *p)
Frees a previously allocated chunk of memory.
Definition: PageArena.h:57
size_t total() const
Statistic accessors - returns total allocated size.
Definition: PageArena.h:357
A memory allocator using std::malloc() and std::free().
Definition: PageArena.h:52
~PageArena()
Destructor releases the whole memory and invalidates all allocated objects.
Definition: PageArena.h:200
void set_page_size(size_t sz)
Sets the page size.
Definition: PageArena.h:210
CharT * dup(const std::string &s)
Duplicate a std::string; memory is allocated from the pool.
Definition: PageArena.h:289
CharT * dup(const void *s, size_t len)
Duplicate a buffer of size len; memory is allocated from the pool.
Definition: PageArena.h:299
std::set< Page *, LtPageRemain > GappyPages
a list of pages with gaps, sorted by gap size
Definition: PageArena.h:120
void swap(Self &other)
Efficiently swaps the state with another allocator.
Definition: PageArena.h:329
size_t m_total
number of pages allocated
Definition: PageArena.h:109
void * allocate(size_t sz)
Allocates a new chunk of memory.
Definition: PageArena.h:54
std::ostream & dump_stat(std::ostream &out) const
Write allocator statistics to output stream.
Definition: PageArena.h:344
bool operator()(const Page *p1, const Page *p2) const
Definition: PageArena.h:114
void free()
Free the whole arena.
Definition: PageArena.h:312
A structure to manage a single memory page which is used to allocate smaller memory chunks...
Definition: PageArena.h:81
struct Hypertable::PageArena::TinyBuffer m_tinybuf
#define HT_EXPECT(_e_, _code_)
Definition: Logger.h:388
CharT * alloc(size_t sz)
Allocate sz bytes.
Definition: PageArena.h:216
#define HT_ASSERT(_e_)
Definition: Logger.h:396
size_t m_page_limit
total number of bytes allocated by users
Definition: PageArena.h:106
#define HT_LIKELY(x)
Definition: compat-c.h:69
CharT * dup(const CharT *s)
Duplicate a null terminated string; memory is allocated from the pool.
Definition: PageArena.h:274
Page * alloc_page(size_t sz, bool prepend=true)
Allocates a page of size sz.
Definition: PageArena.h:145
size_t m_pages
page size in number of bytes
Definition: PageArena.h:108
Logging routines and macros.
PageArena(size_t page_size=DEFAULT_PAGE_SIZE, const PageAllocatorT &alloc=PageAllocatorT())
Constructor; creates a new PageArena.
Definition: PageArena.h:189
CharT * alloc_big(size_t sz)
Definition: PageArena.h:177
The PageArena allocator is simple and fast, avoiding individual mallocs/frees.
Definition: PageArena.h:69
__nat swap(__any, __any)
Hypertable definitions
size_t used() const
Statistic accessors - returns used bytes.
Definition: PageArena.h:351
void freed(size_t sz)
Inform the allocator that a bulk of objects was freed by the caller.
Definition: PageArena.h:60
void copy(TableDumper &, CellsBuilder &)
Definition: TableDumper.cc:129
Page(const char *end)
Definition: PageArena.h:87
PageArena CharArena
Definition: PageArena.h:360
CharT * realloc(void *p, size_t oldsz, size_t newsz)
Realloc for newsz bytes.
Definition: PageArena.h:255
size_t remain() const
Definition: PageArena.h:91
#define HT_UNLIKELY(x)
Definition: compat-c.h:70
bool is_normal_overflow(size_t sz)
Definition: PageArena.h:173
GappyPages m_gappy_pages
Definition: PageArena.h:121
PageArena< CharT, PageAllocatorT > Self
Definition: PageArena.h:76
predicate which sorts by size
Definition: PageArena.h:113
size_t page_size()
Returns the page size.
Definition: PageArena.h:205
size_t pages() const
Statistic accessors - returns number of allocated pages.
Definition: PageArena.h:354
size_t m_page_size
capacity in bytes of an empty page
Definition: PageArena.h:107
PageAllocatorT m_page_allocator
total number of bytes occupied by pages
Definition: PageArena.h:110
CharT * alloc(size_t sz)
Definition: PageArena.h:95
A small buffer (128 bytes) to satisfy very small memory allocations without allocating a page from th...
Definition: PageArena.h:128
PageArena< unsigned char > ByteArena
Definition: PageArena.h:361