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.
Wrap an allocator to check for memory leaks.
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.
Custom allocator that uses nda::mem::malloc to allocate memory.
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.
Custom allocator that uses multiple nda::mem::bucket allocators.
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.
Custom allocator that dispatches memory allocation to one of two allocators based on the size of the ...
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,...
Wrap an allocator to gather statistics about memory allocation.
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.