43#if defined(__has_feature)
44#if __has_feature(address_sanitizer)
45#include <sanitizer/asan_interface.h>
70 template <AddressSpace AdrSp = Host>
110 if constexpr (AdrSp == mem::Host) {
111 return {(
char *)std::calloc(s, 1 ), s};
136 template <
int ChunkSize>
139 std::unique_ptr<char[]> _start = std::make_unique<char[]>(
TotalChunkSize);
142 char *p = _start.get();
145 uint64_t flags = uint64_t(-1);
181 if (s > ChunkSize) std::abort();
182 if (flags == 0) std::abort();
185 int pos = __builtin_ctzll(flags);
188 flags &= ~(1ull << pos);
189 blk_t b{p +
static_cast<ptrdiff_t
>(pos * ChunkSize), s};
191 __asan_unpoison_memory_region(b.ptr, ChunkSize);
204 std::memset(blk.ptr, 0, s);
214 __asan_poison_memory_region(b.ptr, ChunkSize);
216 int pos = (b.ptr - p) / ChunkSize;
217 flags |= (1ull << pos);
224 [[nodiscard]]
bool is_full() const noexcept {
return flags == 0; }
230 [[nodiscard]]
bool empty() const noexcept {
return flags == uint64_t(-1); }
236 [[nodiscard]]
const char *
data() const noexcept {
return p; }
242 [[nodiscard]]
auto mask() const noexcept {
return flags; }
263 template <
int ChunkSize>
269 std::vector<b_t> bu_vec;
272 typename std::vector<b_t>::iterator bu;
275 [[gnu::noinline]]
void find_non_full_bucket() {
276 bu = std::find_if(bu_vec.begin(), bu_vec.end(), [](
auto const &b) { return !b.is_full(); });
277 if (bu != bu_vec.end())
return;
279 auto pos = std::upper_bound(bu_vec.begin(), bu_vec.end(), b, [](
auto const &b1,
auto const &b2) { return b1.data() < b2.data(); });
280 bu = bu_vec.insert(pos, std::move(b));
309 if ((bu == bu_vec.end()) or (bu->is_full())) find_non_full_bucket();
310 return bu->allocate(s);
322 std::memset(blk.ptr, 0, s);
336 if (bu != bu_vec.end() and bu->owns(b)) {
342 bu = std::lower_bound(bu_vec.begin(), bu_vec.end(), b.ptr, [](
auto const &b1,
auto p) { return b1.data() <= p; });
344 EXPECTS_WITH_MESSAGE((bu != bu_vec.end()),
"Error in nda::mem::multi_bucket::deallocate: Owning bucket not found");
345 EXPECTS_WITH_MESSAGE((bu->owns(b)),
"Error in nda::mem::multi_bucket::deallocate: Owning bucket not found");
349 if (!bu->empty())
return;
350 if (bu_vec.size() <= 1)
return;
359 [[nodiscard]]
bool empty() const noexcept {
return bu_vec.size() == 1 && bu_vec[0].empty(); }
365 [[nodiscard]]
auto const &
buckets() const noexcept {
return bu_vec; }
375 for (
const auto &mb : bu_vec) { res = res || mb.owns(b); }
390 template <
size_t Threshold, Allocator A, Allocator B>
399 static_assert(A::address_space == B::address_space);
426 blk_t allocate(
size_t s)
noexcept {
return s <= Threshold ? small.allocate(s) : big.allocate(s); }
435 blk_t allocate_zero(
size_t s)
noexcept {
return s <= Threshold ? small.allocate_zero(s) : big.allocate_zero(s); }
443 void deallocate(
blk_t b)
noexcept {
return b.s <= Threshold ? small.deallocate(b) : big.deallocate(b); }
451 [[nodiscard]]
bool owns(
blk_t b)
const noexcept {
return small.owns(b) or big.owns(b); }
463 template <Allocator A>
466 long memory_used = 0;
494 std::cerr <<
"Memory leak in allocator: " << memory_used <<
" bytes leaked\n";
507 blk_t b = A::allocate(s);
519 blk_t b = A::allocate_zero(s);
531 if (memory_used < 0) {
533 std::cerr <<
"Memory used by allocator < 0: Memory block to be deleted: b.s = " << b.s <<
", b.ptr = " << (
void *)b.ptr <<
"\n";
544 [[nodiscard]]
bool empty()
const {
return (memory_used == 0); }
552 [[nodiscard]]
bool owns(
blk_t b)
const noexcept {
return A::owns(b); }
570 template <Allocator A>
573 std::vector<uint64_t> hist = std::vector<uint64_t>(65, 0);
609 ++hist[__builtin_clzl(s)];
610 return A::allocate(s);
621 ++hist[__builtin_clzl(s)];
622 return A::allocate_zero(s);
637 [[nodiscard]]
bool owns(
blk_t b)
const noexcept {
return A::owns(b); }
643 [[nodiscard]]
auto const &
histogram() const noexcept {
return hist; }
650 os <<
"Allocation size histogram :\n";
651 os <<
"[0, 2^0): " << hist.back() <<
"\n";
652 for (
int i = 0; i < 64; ++i) { os <<
"[2^" << i <<
", 2^" << i + 1 <<
"): " << hist[63 - i] <<
"\n"; }
Provides definitions and type traits involving the different memory address spaces supported by nda.
Custom allocator that allocates a bucket of memory on the heap consisting of 64 chunks.
bucket(bucket &&)=default
Default move constructor.
bool empty() const noexcept
Check if the bucket is empty.
bucket()=default
Default constructor.
bool owns(blk_t b) const noexcept
Check if a given nda::mem::blk_t memory block is owned by the bucket.
void deallocate(blk_t b) noexcept
Deallocate a chunk of memory from the bucket by simply resetting the bitmask.
bool is_full() const noexcept
Check if the bucket is full.
auto mask() const noexcept
Get the bitmask of the bucket.
const char * data() const noexcept
Get a pointer to the start of the bucket.
static constexpr auto address_space
Only Host nda::mem::AddressSpace is supported for this allocator.
bucket(bucket const &)=delete
Deleted copy constructor.
bucket & operator=(bucket const &)=delete
Deleted copy assignment operator.
bucket & operator=(bucket &&)=default
Default move assignment operator.
blk_t allocate(size_t s) noexcept
Allocate a chunk of memory in the bucket and update the bitmask.
static constexpr int TotalChunkSize
Total size of the bucket in bytes.
blk_t allocate_zero(size_t s) noexcept
Allocate a chunk of memory in the bucket, set it to zero and update the bitmask.
bool empty() const
Check if the base allocator is empty.
long get_memory_used() const noexcept
Get the total memory used by the base allocator.
bool owns(blk_t b) const noexcept
Check if a given nda::mem::blk_t memory block is owned by the base allocator.
leak_check & operator=(leak_check &&)=default
Default move assignment operator.
static constexpr auto address_space
nda::mem::AddressSpace in which the memory is allocated.
leak_check(leak_check const &)=delete
Deleted copy constructor.
~leak_check()
Destructor that checks for memory leaks.
blk_t allocate_zero(size_t s)
Allocate memory, set it to zero and update the total memory used.
leak_check()=default
Default constructor.
blk_t allocate(size_t s)
Allocate memory and update the total memory used.
leak_check & operator=(leak_check const &)=delete
Deleted copy assignment operator.
void deallocate(blk_t b) noexcept
Deallocate memory and update the total memory used.
leak_check(leak_check &&)=default
Default move constructor.
mallocator & operator=(mallocator const &)=delete
Deleted copy assignment operator.
static void deallocate(blk_t b) noexcept
Deallocate memory using nda::mem::free.
mallocator & operator=(mallocator &&)=default
Default move assignment operator.
mallocator()=default
Default constructor.
mallocator(mallocator const &)=delete
Deleted copy constructor.
static blk_t allocate_zero(size_t s) noexcept
Allocate memory and set it to zero.
static blk_t allocate(size_t s) noexcept
Allocate memory using nda::mem::malloc.
static constexpr auto address_space
nda::mem::AddressSpace in which the memory is allocated.
mallocator(mallocator &&)=default
Default move constructor.
multi_bucket(multi_bucket &&)=default
Default move constructor.
bool owns(blk_t b) const noexcept
Check if a given nda::mem::blk_t memory block is owned by allocator.
blk_t allocate_zero(size_t s) noexcept
Allocate a chunk of memory in the current bucket or find a new one if the current one is full and set...
void deallocate(blk_t b) noexcept
Deallocate a chunk of memory from the bucket to which it belongs.
multi_bucket()
Default constructor.
bool empty() const noexcept
Check if the current allocator is empty.
static constexpr auto address_space
Only Host nda::mem::AddressSpace is supported for this allocator.
multi_bucket & operator=(multi_bucket &&)=default
Default move assignment operator.
auto const & buckets() const noexcept
Get the bucket vector.
blk_t allocate(size_t s) noexcept
Allocate a chunk of memory in the current bucket or find a new one if the current one is full.
multi_bucket(multi_bucket const &)=delete
Deleted copy constructor.
multi_bucket & operator=(multi_bucket const &)=delete
Deleted copy assignment operator.
blk_t allocate_zero(size_t s) noexcept
Allocate memory and set the memory to zero using the small allocator if the size is less than or equa...
segregator()=default
Default constructor.
segregator(segregator const &)=delete
Deleted copy constructor.
void deallocate(blk_t b) noexcept
Deallocate memory using the small allocator if the size is less than or equal to the Threshold,...
bool owns(blk_t b) const noexcept
Check if a given nda::mem::blk_t memory block is owned by the allocator.
segregator & operator=(segregator &&)=default
Default move assignment operator.
segregator(segregator &&)=default
Default move constructor.
static constexpr auto address_space
nda::mem::AddressSpace in which the memory is allocated.
segregator & operator=(segregator const &)=delete
Deleted copy assignment operator.
blk_t allocate(size_t s) noexcept
Allocate memory using the small allocator if the size is less than or equal to the Threshold,...
stats & operator=(stats &&)=default
Default move assignment operator.
static constexpr auto address_space
nda::mem::AddressSpace in which the memory is allocated.
blk_t allocate(uint64_t s)
Allocate memory and update the histogram.
auto const & histogram() const noexcept
Get the histogram of the allocation sizes.
void print_histogram(std::ostream &os) const
Print the histogram to a std::ostream.
blk_t allocate_zero(uint64_t s)
Allocate memory, set it to zero and update the histogram.
~stats()
Destructor that outputs the statistics about the memory allocation in debug mode.
stats & operator=(stats const &)=delete
Deleted copy assignment operator.
bool owns(blk_t b) const noexcept
Check if a given nda::mem::blk_t memory block is owned by the base allocator.
stats(stats const &)=delete
Deleted copy constructor.
void deallocate(blk_t b) noexcept
Deallocate memory.
stats(stats &&)=default
Default move constructor.
stats()=default
Default constructor.
void * malloc(size_t size)
Call the correct malloc function based on the given address space.
void memset(void *p, int value, size_t count)
Call the correct memset function based on the given address space.
void free(void *p)
Call the correct free function based on the given address space.
Macros used in the nda library.
Provides a generic malloc and free function for different address spaces.
Provides a generic memset and memset2D function for different address spaces.
Memory block consisting of a pointer and its size.
char * ptr
Pointer to the memory block.
size_t s
Size of the memory block in bytes.