-
Notifications
You must be signed in to change notification settings - Fork 0
/
bump_allocator.hpp
278 lines (229 loc) · 7.09 KB
/
bump_allocator.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
#ifndef BUMP_ALLOCATOR_INCLUDED
#define BUMP_ALLOCATOR_INCLUDED 1
// Bump Allocation. Usable but has some noisy output and should be
// improved in a variaty of ways, but good enough for use here.
// Based on ideas by Howard Hinnant, specially short_alloc.h.
//
// Copyright (c) 2018 Melissa O'Neill. MIT License, but should not
// be used in other code without futher refinement.
#include <new>
#include <memory>
#include <typeinfo>
#include <cxxabi.h>
#include <forward_list>
#include <unistd.h>
#include <sys/mman.h>
template <typename T>
static const char* name_for_type()
{
static char* buffer = (char*) malloc(512);
static size_t buffer_size = 512;
return abi::__cxa_demangle(typeid(T).name(), buffer, &buffer_size,
nullptr);
}
static void* large_alloc(size_t size)
{
#if USE_BIG_PAGES
#ifndef MAP_HUGETLB
#error No Huge/Super page support
#endif
void* alloc = mmap(0, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB, 0, 0);
if (alloc == MAP_FAILED)
throw std::bad_alloc();
return alloc;
#else
return ::operator new(size);
#endif
}
static void large_free(void* addr, size_t size)
{
#if USE_BIG_PAGES
int err = munmap(addr, size);
assert(err == 0);
#else
return ::operator delete(addr);
#endif
}
template <std::size_t N, std::size_t alignment = alignof(std::max_align_t)>
class mempool
{
static_assert(alignment <= alignof(std::max_align_t),
"Alignment can be at most that of std::max_align_t");
char* ptr_;
char* end_;
std::forward_list<char*> chunks_;
public:
~mempool() {
ptr_ = nullptr;
end_ = nullptr;
while (!chunks_.empty()) {
auto chunkptr = chunks_.front();
chunks_.pop_front();
fprintf(stderr, "- mempool at %p deallocated chunk at %p\n", this, chunkptr);
large_free(chunkptr, N);
}
}
mempool() noexcept
{
alloc_chunk();
}
void alloc_chunk()
{
ptr_ = static_cast<char*>(large_alloc(N));
end_ = ptr_ + N;
chunks_.push_front(ptr_);
fprintf(stderr, "- mempool at %p allocated new chunk at %p\n", this, ptr_);
}
mempool(const mempool&) = delete;
mempool& operator=(const mempool&) = delete;
template <std::size_t ReqAlign> void* allocate(std::size_t n);
void deallocate(void* p, std::size_t n) noexcept;
static constexpr std::size_t size() noexcept
{
return N;
}
std::size_t remaining() const noexcept
{
return static_cast<std::size_t>(end_ - ptr_);
}
private:
static std::size_t align_up(std::size_t n) noexcept
{
return (n + (alignment-1)) & ~(alignment-1);
}
};
template <std::size_t N, std::size_t alignment>
template <std::size_t ReqAlign>
void* mempool<N, alignment>::allocate(std::size_t n)
{
static_assert(ReqAlign <= alignment, "alignment is too small for this arena");
assert(ptr_ != nullptr && "short_alloc has outlived arena");
if (n > N/2) {
auto result = large_alloc(n);
fprintf(stderr, "- mempool at %p: large alloc %zd bytes at %p.\n",
this, n, result);
return result;
}
auto bumped_ptr = ptr_ + align_up(n);
if (bumped_ptr > end_) {
alloc_chunk();
bumped_ptr = ptr_ + align_up(n);
assert((bumped_ptr <= end_) && "odd: can't happen");
}
void* alloc = static_cast<void*>(ptr_);
ptr_ = bumped_ptr;
return alloc;
}
template <std::size_t N, std::size_t alignment>
void mempool<N, alignment>::deallocate(void* p, std::size_t n) noexcept
{
if (n > N/2) {
fprintf(stderr, "- mempool at %p: large dealloc %zd bytes at %p.\n",
this, n, p);
large_free(p, n);
}
}
mempool<(size_t(1) << 30),8> global_mempool;
template <typename T>
struct bump_allocator_static {
size_t allocator_count_ = 0;
size_t total_allocations_ = 0;
size_t active_allocations_ = 0;
size_t high_allocations_ = 0;
size_t total_mem_ = 0;
size_t active_mem_ = 0;
size_t high_mem_ = 0;
// std::allocator<T> std_alloc_;
T* allocate(size_t n, const void *hint=0)
{
++total_allocations_;
++active_allocations_;
if (active_allocations_ > high_allocations_)
high_allocations_ = active_allocations_;
size_t bytes = n*sizeof(T);
total_mem_ += bytes;
active_mem_ += bytes;
if (active_mem_ > high_mem_)
high_mem_ = active_mem_;
// return std_alloc_.allocate(n, hint);
auto result = global_mempool.allocate<alignof(T)>(bytes);
// fprintf(stderr, "- %p: alloc %zd bytes at %p.\n", this, bytes, result);
return static_cast<T*>(result);
}
void deallocate(T* p, size_t n)
{
--active_allocations_;
size_t bytes = n*sizeof(T);
active_mem_ -= bytes;
// fprintf(stderr, "- %p: dealloc %zd bytes at %p.\n", this, bytes, p);
// std_alloc_.deallocate(p, n);
global_mempool.deallocate(p, bytes);
}
bump_allocator_static() noexcept
{
fprintf(stderr, "- bump_allocator_static< %s > (item size = %zu) at %p created!\n",
name_for_type<T>(), sizeof(T), this);
}
~bump_allocator_static() noexcept
{
fprintf(stderr, "- bump_allocator_static< %s > at %p destroyed!\n",
name_for_type<T>(), this);
fprintf(stderr, " - total_allocations_ = %zu\n", total_allocations_);
fprintf(stderr, " - active_allocations_ = %zu\n", active_allocations_);
fprintf(stderr, " - high_allocations_ = %zu\n", high_allocations_);
fprintf(stderr, " - total_mem_ = %zu\n", total_mem_);
fprintf(stderr, " - active_mem_ = %zu\n", active_mem_);
fprintf(stderr, " - high_mem_ = %zu\n", high_mem_);
}
};
template <typename T>
class bump_allocator
{
private:
static bump_allocator_static<T> real_allocator_;
template <typename U> friend class bump_allocator;
public:
typedef T value_type;
typedef std::false_type propagate_on_container_copy_assignment;
typedef std::true_type propagate_on_container_move_assignment;
typedef std::true_type propagate_on_container_swap;
typedef std::false_type is_always_equal;
T* allocate(size_t n, const void *hint=0)
{
return real_allocator_.allocate(n, hint);
}
void deallocate(T* p, size_t n)
{
return real_allocator_.deallocate(p,n);
}
//#define dbg2_printf(...) fprintf(stderr, __VA_ARGS__)
#define dbg2_printf(...)
bump_allocator() noexcept {
dbg2_printf("- bump_allocator< %s > (item size = %zu) at %p created!\n",
name_for_type<T>(), sizeof(T), this);
}
bump_allocator(const bump_allocator& orig) noexcept
{
dbg2_printf("- bump_allocator< %s > (item size = %zu) at %p copied from %p!\n",
name_for_type<T>(), sizeof(T), this, &orig);
// Nothing (else) to do.
}
template <class U>
bump_allocator(const bump_allocator<U>& orig) noexcept
{
dbg2_printf("- bump_allocator< %s > (item size = %zu) at %p created based on %p\n",
name_for_type<T>(), sizeof(T), this, &orig);
// Nothing (else) to do.
}
~bump_allocator() noexcept
{
dbg2_printf("- bump_allocator< %s > at %p destroyed!\n",
name_for_type<T>(), this);
}
};
template <typename T>
bump_allocator_static<T> bump_allocator<T>::real_allocator_;
template <typename T>
using unordered_bump_set =
std::unordered_set<T, std::hash<T>, std::equal_to<T>, bump_allocator<T>>;
#endif // BUMP_ALLOCATOR_INCLUDED