20static constexpr size_t MINIMUM_HUGEPAGE = 2 * 1024 * 1024;
41 void* bump_alloc (
size_t size);
42 size_t grow_spans (
size_t needed,
bool preallocating);
43 size_t free_block ()
const;
45 size_t totalmem ()
const {
return totalmem_; }
47 char* memmap (
size_t size,
bool growsup);
48 char* remap (
void *addr,
size_t oldsize,
size_t size);
50 char *mmstart =
nullptr;
51 boost::atomic<uintptr_t> offset = 0;
52 boost::atomic<uintptr_t> mmsize = 0;
53 boost::atomic<MMSpan*> next =
nullptr;
55 boost::atomic<size_t> totalmem_ = 0;
56 boost::atomic<MMSpan*> spans_ =
nullptr;
61BumpAllocator::list_arenas (
ArenaList &arenas)
63 for (MMSpan *lspan = spans_; lspan; lspan = lspan->next)
64 arenas.
push_back ({ uintptr_t (lspan->mmstart), lspan->offset, lspan->mmsize });
68BumpAllocator::free_block ()
const
71 MMSpan *lspan = spans_;
72 while (lspan && lspan->next)
76 return lspan->mmsize - lspan->offset;
80BumpAllocator::grow_spans (
size_t needed,
bool preallocating)
82 const size_t entry_total = totalmem_;
84 if (entry_total < totalmem_)
89 MMSpan *lspan = spans_;
90 while (lspan && lspan->next)
92 totalmem += lspan->mmsize;
97 const size_t oldsize = lspan->mmsize;
98 size_t newsize = oldsize * 2;
99 while (newsize < needed)
101 char *mmstart = remap (lspan->mmstart, oldsize, newsize);
102 if (mmstart == lspan->mmstart)
104 if (config_flags & PREFAULT_PAGES)
105 for (
size_t i = oldsize; i < newsize; i +=
PAGESIZE)
107 lspan->mmsize = newsize;
108 totalmem_ = totalmem + newsize;
109 VDEBUG (
"%s:%u: new-total=%dM: true\n", __func__, __LINE__, totalmem_ / (1024 * 1024));
110 return newsize - oldsize;
114 size_t mmsize = MINIMUM_HUGEPAGE;
115 while (mmsize < needed)
117 mmsize = MEM_ALIGN (mmsize, MINIMUM_HUGEPAGE);
118 char *mmstart = memmap (mmsize, !lspan);
121 VDEBUG (
"%s:%u: false\n", __func__, __LINE__);
124 for (
size_t i = 0; i < mmsize; i +=
PAGESIZE)
126 MMSpan *nspan =
new (mmstart) MMSpan();
127 nspan->offset = MEM_ALIGN (
sizeof (MMSpan), 64);
128 nspan->mmstart = mmstart;
129 nspan->mmsize = mmsize;
134 totalmem_ = totalmem + nspan->mmsize;
135 VDEBUG (
"%s:%u: new-total=%dM: true\n", __func__, __LINE__, totalmem_ / (1024 * 1024));
140BumpAllocator::remap (
void *addr,
size_t oldsize,
size_t size)
142 assert_return (MEM_ALIGN (size, MINIMUM_HUGEPAGE) == size,
nullptr);
145 char *memory = (
char*) mremap (addr, oldsize, size, 0, addr);
146 if (memory == MAP_FAILED)
148 VDEBUG (
"%s:%u: mremap: %p %d %d: %s\n", __func__, __LINE__, addr, oldsize, size, strerror (errno));
151 VDEBUG (
"%s:%u: mremap: %p %d %d: %p\n", __func__, __LINE__, addr, oldsize, size, memory);
152 if (madvise (memory, size, MADV_HUGEPAGE) < 0)
153 VDEBUG (
"%s: madvise(%p,%u,MADV_HUGEPAGE) failed: %s\n", __func__, memory, size, strerror (errno));
158BumpAllocator::memmap (
size_t size,
bool growsup)
160 assert_return (MEM_ALIGN (size, MINIMUM_HUGEPAGE) == size,
nullptr);
161 const int PROTECTION = PROT_READ | PROT_WRITE;
162 const int PRIVANON = MAP_PRIVATE | MAP_ANONYMOUS;
164 const bool anon_hugepages =
true;
165 const bool reserved_hugepages =
false;
168 void *addr =
nullptr;
169 const size_t g1 = 1024 * 1024 * 1024;
170 const size_t gigabytes =
sizeof (
size_t) <= 4 ? 3 * g1 : 256 * g1;
171 for (
size_t giga = gigabytes; giga >= 16 * 1024 * 1024; giga = giga >> 1)
172 if ((addr = mmap (
nullptr, giga, 0 * PROTECTION, PRIVANON, -1, 0)) != MAP_FAILED)
174 VDEBUG (
"%s:%d: addr-hint size=%uMB: %p\n", __FILE__, __LINE__, giga / (1024 * 1024), addr);
178 if (addr == MAP_FAILED)
183 if (reserved_hugepages)
185 memory = (
char*) mmap (addr, size, PROTECTION, PRIVANON | MAP_HUGETLB, -1, 0);
186 if (memory != MAP_FAILED)
188 VDEBUG (
"%s:%u: mmap: %p %d HUGETLB: %p\n", __func__, __LINE__, addr, size, memory);
191 VDEBUG (
"%s:%u: mmap: %p %d HUGETLB: %s\n", __func__, __LINE__, addr, size, strerror (errno));
195 size_t areasize =
size + MINIMUM_HUGEPAGE;
196 memory = (
char*) mmap (addr, areasize, PROTECTION, PRIVANON, -1, 0);
197 if (memory == MAP_FAILED)
199 VDEBUG (
"%s:%u: mmap: %p %d: %s\n", __func__, __LINE__, addr, areasize, strerror (errno));
202 VDEBUG (
"%s:%u: mmap: %p %d: %p\n", __func__, __LINE__, addr, areasize, memory);
205 size_t extra = MEM_ALIGN (start, MINIMUM_HUGEPAGE) - start;
206 if (extra && munmap (memory, extra) != 0)
207 VDEBUG (
"%s: munmap(%p,%u) failed: %s\n", __func__, memory, extra, strerror (errno));
211 extra = areasize -
size;
213 if (extra && munmap (memory + areasize, extra) != 0)
214 VDEBUG (
"%s: munmap(%p,%u) failed: %s\n", __func__, memory + areasize, extra, strerror (errno));
216 VDEBUG (
"%s:%u: mmap page-align: size=%d at: %p\n", __func__, __LINE__, size, memory);
219 if (anon_hugepages && madvise (memory, size, MADV_HUGEPAGE) < 0)
220 VDEBUG (
"%s: madvise(%p,%u,MADV_HUGEPAGE) failed: %s\n", __func__, memory, size, strerror (errno));
225BumpAllocator::bump_alloc (
size_t size)
228 for (MMSpan *span = spans_; span; span = span->next)
233 nmark = omark +
size;
234 if (nmark > span->mmsize)
237 while (!span->offset.compare_exchange_weak (omark, nmark));
240 span->mmsize - span->offset < config_watermark)
243 if (0 == config_lowmem_notified++)
250 return span->mmstart + omark;
254 const size_t old_totalmem_ = totalmem_;
255 grow_spans (size,
false);
256 if (config_willgrow) {
257 std::function<void(
size_t,
size_t)> *willgrow = config_willgrow;
258 (*willgrow) (old_totalmem_,
size);
261 return bump_alloc (size);
265static constexpr unsigned SMALL_BLOCK_LIMIT = 8192;
266static constexpr unsigned SMALL_BLOCK_BUCKETS = SMALL_BLOCK_LIMIT / 64;
267static constexpr unsigned NUMBER_OF_POWER2_BUCKETS =
sizeof (
uintptr_t) * 8;
268static constexpr unsigned NUMBER_OF_BUCKETS = NUMBER_OF_POWER2_BUCKETS + SMALL_BLOCK_BUCKETS;
272bucket_size (
unsigned index)
274 if (index < NUMBER_OF_POWER2_BUCKETS)
276 return (index - NUMBER_OF_POWER2_BUCKETS + 1) * 64;
280static inline unsigned
281bucket_index (
unsigned long long n)
284 if (n <= SMALL_BLOCK_LIMIT)
285 return NUMBER_OF_POWER2_BUCKETS + (n - 1) / 64;
286 constexpr unsigned CLZLL_BITS =
sizeof (0ull) * 8;
287 const unsigned upper_power2_shift = CLZLL_BITS - __builtin_clzll (n - 1);
288 return upper_power2_shift;
303 void do_free (
void *mem,
size_t size);
304 size_t count (
unsigned bucket_index,
const ArenaList &arenas);
308 boost::atomic<Block*> intr_ptr_ =
nullptr;
310 static_assert (
sizeof (Block) <= 64);
313 CANARY0 = 0xbe4da62f087c3519ull
315 bool maybeok (
const Block *block,
const ArenaList &arenas);
321LoftBuckets::do_alloc (
size_t size,
size_t align)
326 const unsigned bindex = bucket_index (size);
327 if (bindex >= NUMBER_OF_BUCKETS)
329 const size_t bsize = bucket_size (bindex);
330 auto block = buckets_[bindex].pop();
334 block = (Block*) bump_allocator.bump_alloc (bsize);
341LoftBuckets::do_free (
void *mem,
size_t size)
344 const unsigned bindex = bucket_index (size);
346 const size_t bsize = bucket_size (bindex);
348 auto block =
new (mem) Block();
349 buckets_[bindex].push (block);
353LoftBuckets::maybeok (
const Block *block,
const ArenaList &arenas)
360 bool contained =
false;
361 for (
const auto &a : arenas)
362 if (addr >= a.addr && addr < a.addr + a.
size)
369 block->canary0 == CANARY0)
375LoftBuckets::count (
unsigned bucket_index,
const ArenaList &arenas)
380 for (Block *block = buckets_[bucket_index].peek(); block; block = block->intr_ptr_.load())
381 if (maybeok (block, arenas))