11#define MEM_ALIGN(addr, alignment) (alignment * size_t ((size_t (addr) + alignment - 1) / alignment))
13#define CHECK_FREE_OVERLAPS 0
15#define CHECK_FREE_OVERLAPS 1
18inline constexpr size_t MINIMUM_ARENA_SIZE = 4 * 1024 * 1024;
19inline constexpr size_t MINIMUM_HUGEPAGE = 2 * 1024 * 1024;
25HugePage::HugePage (
void *m,
size_t s) :
31 ReleaseF release_ =
nullptr;
33 void free_start () {
free (start_); }
34 void unadvise_free_start () { madvise (start_, size_, MADV_NOHUGEPAGE); free_start(); }
35 void unadvise_munmap_start () { madvise (start_, size_, MADV_NOHUGEPAGE); munmap_start(); }
44 auto release = release_;
54 assert_return (0 == (minimum_alignment & (minimum_alignment - 1)), {});
55 constexpr int protection = PROT_READ | PROT_WRITE;
56 constexpr int flags = MAP_PRIVATE | MAP_ANONYMOUS;
58 if (bytelength == MEM_ALIGN (bytelength, MINIMUM_HUGEPAGE) && minimum_alignment <= MINIMUM_HUGEPAGE)
60 void *memory =
mmap (
nullptr, bytelength, protection, flags | MAP_HUGETLB, -1, 0);
61 if (memory != MAP_FAILED)
63 assert_return ((
size_t (memory) & (minimum_alignment - 1)) == 0, {});
65 const int mlret =
mlock (memory, bytelength);
67 printerr (
"%s: mlock(%p,%u) failed: %s\n", __func__, memory, bytelength,
strerror (
errno));
72 if (bytelength == MEM_ALIGN (bytelength,
std::max (minimum_alignment, MINIMUM_HUGEPAGE)))
74 static const size_t pagesize =
sysconf (_SC_PAGESIZE);
75 minimum_alignment =
std::max (minimum_alignment, MINIMUM_HUGEPAGE);
76 size_t areasize = minimum_alignment - pagesize + bytelength;
77 char *memory = (
char*)
mmap (
nullptr, areasize, protection, flags, -1, 0);
82 size_t extra = MEM_ALIGN (start, minimum_alignment) - start;
83 if (extra &&
munmap (memory, extra) != 0)
84 printerr (
"%s: munmap(%p,%u) failed: %s\n", __func__, memory, extra,
strerror (
errno));
88 extra = areasize -
size_t (areasize / minimum_alignment) * minimum_alignment;
90 if (extra &&
munmap (memory + areasize, extra) != 0)
91 printerr (
"%s: munmap(%p,%u) failed: %s\n", __func__, memory + areasize, extra,
strerror (
errno));
94 assert_return ((
size_t (memory) & (minimum_alignment - 1)) == 0, {});
96 const int mlret =
mlock (memory, bytelength);
98 printerr (
"%s: mlock(%p,%u) failed: %s\n", __func__, memory, bytelength,
strerror (
errno));
99 LinuxHugePage::ReleaseF release;
101 if (madvise (memory, areasize, MADV_HUGEPAGE) >= 0)
102 release = &LinuxHugePage::unadvise_munmap_start;
104 release = &LinuxHugePage::munmap_start;
109 if (bytelength == MEM_ALIGN (bytelength, MINIMUM_HUGEPAGE) && minimum_alignment <= MINIMUM_HUGEPAGE)
114 assert_return ((
size_t (memory) & (minimum_alignment - 1)) == 0, {});
115 LinuxHugePage::ReleaseF release;
117 if (madvise (memory, bytelength, MADV_HUGEPAGE) >= 0)
118 release = &LinuxHugePage::unadvise_free_start;
120 release = &LinuxHugePage::free_start;
128 assert_return ((
size_t (memory) & (minimum_alignment - 1)) == 0, {});
137 void reset (
uint32 sz = 0) { start = 0; length = sz; }
138 void zero (
char *area)
const {
memset (__builtin_assume_aligned (area + start,
cache_line_size), 0, length); }
145 const uint32 mem_alignment;
147 blob (newblob), mem_alignment (alignment)
151 assert_return ((
size_t (blob->mem()) & (blob->alignment() - 1)) == 0);
152 if (size() >= 1024 * 1024)
153 extents.reserve (1024);
156 area.zero (blob->mem());
162 const size_t s = sum();
163 if (s != blob->size())
164 warning (
"%s:%s: deleting area while bytes are unreleased: %zd", __FILE__, __func__, blob->size() - s);
181 for (
const auto b : extents)
190 ext.zero (blob->mem());
191 ssize_t overlaps_existing = -1, before = -1, after = -1;
192 for (
size_t i = 0; i < extents.size(); i++)
193 if (ext.start == extents[i].start + extents[i].length)
199 else if (ext.start + ext.length == extents[i].start)
205 else if (CHECK_FREE_OVERLAPS &&
206 ext.start + ext.length > extents[i].start &&
207 ext.start < extents[i].start + extents[i].length)
208 overlaps_existing = i;
213 extents[after].length += ext.length;
216 extents[after].length += extents[before].length;
217 extents.erase (extents.begin() + before);
223 extents[before].length += ext.length;
224 extents[before].start = ext.start;
228 extents.push_back (ext);
231 best_fit (
size_t length)
const
234 for (
size_t j = 0; j < extents.size(); j++)
236 const size_t i = extents.size() - 1 - j;
237 if (
ISLIKELY (length > extents[i].length))
239 if (
ISLIKELY (length == extents[i].length))
243 ISLIKELY (extents[i].length < extents[candidate].length) or
244 (
ISLIKELY (extents[i].length == extents[candidate].length) and
245 ISLIKELY (extents[i].start < extents[candidate].start)))
255 const uint32 aligned_length = MEM_ALIGN (ext.length, mem_alignment);
257 const ssize_t candidate = best_fit (aligned_length);
261 ext.start = extents[candidate].start;
262 ext.length = aligned_length;
263 if (
UNLIKELY (extents[candidate].length > aligned_length))
265 extents[candidate].start += aligned_length;
266 extents[candidate].length -= aligned_length;
271 extents[candidate] = extents.back();
272 extents.resize (extents.size() - 1);
283 return a.start < b.start;
285 std::sort (extents.begin(), extents.end(), isless_start);
286 for (
size_t i = extents.size() - 1; i > 0; i--)
287 if (extents[i-1].start + extents[i-1].length == extents[i].start)
289 extents[i-1].length += extents[i].length;
290 extents.erase (extents.begin() + i);
306 allocator (
const Arena &base)
312static Allocator* fmallocator (
const Arena &a) {
return FastMemoryArena::allocator (a); }
323 mem_size = MEM_ALIGN (mem_size, alignment);
326 fatal_error (
"ASE: failed to allocate aligned memory (%u bytes): %s", mem_size, strerror (errno));
328 return FastMemoryArena (fmap);
336 *
this = create_arena (
alignment, mem_size);
348 return fma ?
fma->size() : 0;
354 return fma ?
fma->mem_alignment : 0;
360 const Block zeroblock = {
nullptr, 0 };
363 Extent32 ext { 0, length };
364 if (
fma->alloc_ext (ext))
365 return Block {
fma->memory() + ext.start, ext.length };
387 const size_t block_offset = ((
char*) ab.block_start) -
fma->memory();
390 fma->release_ext (ext);
400static constexpr bool trace_NewDeleteBase =
false;
405 if (trace_NewDeleteBase)
406 Ase::printerr (
"del: %p (%d, %d)\n", ptr, sz, al);
418 ptr = fast_mem_alloc (sz);
419 if (trace_NewDeleteBase)
420 Ase::printerr (
"new: %p (%d, %d)\n", ptr, sz, al);
431 void *block_start =
nullptr;
436 block_start (ptr), block_length (length), arena_index (index)
442 Block block ()
const {
return { block_start, block_length }; }
446fast_mem_allocate_aligned_block_L (
uint32 length)
450 for (
uint32 i = 0; i < fast_mem_arenas.size(); i++)
453 if (
fma->alloc_ext (ext))
455 void *
const ptr =
fma->memory() + ext.start;
456 return { ptr, ext.length, i };
462 const uint32 arena_index = fast_mem_arenas.size();
463 fast_mem_arenas.push_back (arena);
465 if (
fma->alloc_ext (ext))
467 void *
const ptr =
fma->memory() + ext.start;
468 return { ptr, ext.length, arena_index };
470 fatal_error (
"newly allocated arena too short for request: %u < %u",
fma->size(), ext.length);
480mm_info_lookup (
void *ptr)
483 const size_t arrsz =
sizeof (mm_info) /
sizeof (mm_info[0]);
485 const uint64_t M = 11400714819323198487ull;
486 const uint64_t S = 0xcbf29ce484222325;
488 hash = (u.a[0] ^ hash) * M;
489 hash = (u.a[1] ^ hash) * M;
490 hash = (u.a[2] ^ hash) * M;
491 hash = (u.a[3] ^ hash) * M;
492 hash = (u.a[4] ^ hash) * M;
493 hash = (u.a[5] ^ hash) * M;
494 hash = (u.a[6] ^ hash) * M;
495 hash = (u.a[7] ^ hash) * M;
496 return mm_info[hash % arrsz];
500mm_info_push_mt (
const ArenaBlock &ablock)
502 MemoryMetaInfo &mi = mm_info_lookup (ablock.block_start);
504 mi.ablocks.push_back (ablock);
508mm_info_pop_mt (
void *block_start)
510 MemoryMetaInfo &mi = mm_info_lookup (block_start);
512 auto it =
std::find_if (mi.ablocks.begin(), mi.ablocks.end(),
513 [block_start] (
const auto &ab) {
514 return ab.block_start == block_start;
516 if (it != mi.ablocks.end())
518 const ArenaBlock ab = *it;
519 if (it < mi.ablocks.end() - 1)
520 *it = mi.ablocks.back();
521 mi.ablocks.resize (mi.ablocks.size() - 1);
531fast_mem_alloc (
size_t size)
536 void *
const ptr = ab.block_start;
538 mm_info_push_mt (ab);
540 fatal_error (
"%s: failed to allocate %u bytes\n", __func__, size);
545fast_mem_free (
void *mem)
550 fatal_error (
"%s: invalid memory pointer: %p\n", __func__, mem);
552 FastMemory::fast_mem_arenas[ab.arena_index].release (ab.block());
557static CString cstring_early_test =
"NULL";
565 bool operator() (
const String *a,
const String *b)
const noexcept {
return *a == *b; }
573 static String empty_string;
574 strings_ = { &empty_string };
575 quarks_[&empty_string] = 0;
585CStringTable::add (
const String &s)
noexcept
588 auto it = quarks_.find (&s);
589 if (it != quarks_.end()) [[likely]]
591 const uint quark = strings_.size();
592 strings_.push_back (
new String (s));
593 quarks_[strings_.back()] = quark;
598CStringTable::find (
const String &s)
noexcept
601 auto it = quarks_.find (&s);
602 if (it == quarks_.end())
return 0;
607CStringTable::lookup (
uint quark)
noexcept
610 if (quark < strings_.size()) [[likely]]
611 return *strings_[quark];
621CString::assign (
const String &s)
noexcept
623 quark_ = CStringTable::the().add (s);
634 cstring.quark_ = CStringTable::the().find (s);
642 return CStringTable::the().lookup (quark_);
646CString::temp_quark_impl (
CString c)
652CString::temp_quark_impl (
uint maybequark)
655 const std::string &stdstring = CStringTable::the().lookup (maybequark);
656 cstring.quark_ = stdstring.
empty() ? 0 : maybequark;
669aligned_allocator_tests()
672 const ssize_t kb = 1024, asz = 4 * 1024;
682 success =
fma.alloc_ext (s1);
685 Extent32 s2 (kb - 1);
686 success =
fma.alloc_ext (s2);
690 success =
fma.alloc_ext (s3);
694 success =
fma.alloc_ext (s4);
698 fma.release_ext (s1);
700 fma.release_ext (s3);
704 success =
fma.alloc_ext (s1);
707 fma.release_ext (s2);
710 success =
fma.alloc_ext (s1);
714 fma.release_ext (s1);
715 fma.release_ext (s4);
720 while (sum < 37 * 1024 * 1024)
723 ptrs.push_back (fast_mem_alloc (sz));
727 while (!ptrs.empty())
729 fast_mem_free (ptrs.back());
736memory_cstring_tests()
740 const bool equality_checks =
741 cstring_early_test ==
CString (
"NULL") &&
742 cstring_early_test ==
String (
"NULL") &&
743 cstring_early_test ==
"NULL" &&
744 cstring_early_test !=
CString (
"u") &&
745 cstring_early_test !=
String (
"u") &&
746 cstring_early_test !=
"u" &&
749 const bool lesser_checks =
755 const bool greater_checks =
796 const char *unique_str =
"Af00-61c34bc5fd7c#nosuchthing";
803 struct TwoCStrings {
CString a, b; };
804 static_assert (
sizeof (TwoCStrings) <= 2 * 4);
817 const CString ac (
"a"), bc (
"b");
827 assert_return (
"foo" == CString::temp_quark_impl (CString::temp_quark_impl (
"foo")));
T aligned_alloc(T... args)
Map std::string <-> uint IDs, thread safe.
Compact, deduplicating string variant for constant strings.
static CString lookup(const std::string &s)
Lookup a previously existing CString for a std::string s.
const std::string & string() const
Convert CString into a std::string.
#define assert_return(expr,...)
Return from the current function if expr is unmet and issue an assertion warning.
#define return_unless(cond,...)
Return silently if cond does not evaluate to true with return value ...
#define UNLIKELY(cond)
Hint to the compiler to optimize for cond == FALSE.
#define TEST_INTEGRITY(FUNC)
Register func as an integrity test.
#define ISLIKELY(cond)
Hint to the compiler to optimize for cond == TRUE.
constexpr size_t cache_line_size
Minimum alignment >= cache line size, see getconf LEVEL1_DCACHE_LINESIZE.
The Anklang C++ API namespace.
std::string string_format(const char *format, const Args &...args) __attribute__((__format__(__printf__
Format a string similar to sprintf(3) with support for std::string and std::ostringstream convertible...
uint64_t uint64
A 64-bit unsigned integer, use PRI*64 in format strings.
int64_t random_irange(int64_t begin, int64_t end)
Generate uniformly distributed pseudo-random integer within range.
std::string String
Convenience alias for std::string.
uint32_t uint32
A 32-bit unsigned integer.
uint32_t uint
Provide 'uint' as convenience type.
Memory area (over-)aligned to cache size and utilizing huge pages.
uint64 location() const
Address of memory area.
size_t alignment() const
Alignment for block addresses and length.
uint64 reserved() const
Reserved memory area in bytes.
Arena(uint32 mem_size, uint32 alignment=cache_line_size)
Create isolated memory area.
Block allocate(uint32 length) const
Create a memory block from cache-line aligned memory area, MT-Unsafe.
void release(Block allocatedblock) const
Realease a previously allocated block, MT-Unsafe.
AllocatorP fma
Identifier for the associated memory allocator.
Reference for an allocated memory block.
Interface to the OS huge page allocator.
static HugePageP allocate(size_t minimum_alignment, size_t bytelength)
Try to allocate a HugePage >= bytelength with minimum_alignment, usual sizes are 2MB.