32#if defined(__has_feature)
33#if __has_feature(address_sanitizer)
34#include <sanitizer/asan_interface.h>
59 template <AddressSpace AdrSp = Host>
99 if constexpr (AdrSp == mem::Host) {
100 return {(
char *)std::calloc(s, 1 ), s};
125 template <
int ChunkSize>
128 std::unique_ptr<char[]> _start = std::make_unique<char[]>(
TotalChunkSize);
131 char *p = _start.get();
134 uint64_t flags = uint64_t(-1);
170 if (s > ChunkSize) std::abort();
171 if (flags == 0) std::abort();
174 int pos = __builtin_ctzll(flags);
177 flags &= ~(1ull << pos);
178 blk_t b{p +
static_cast<ptrdiff_t
>(pos * ChunkSize), s};
180 __asan_unpoison_memory_region(b.ptr, ChunkSize);
193 std::memset(blk.ptr, 0, s);
203 __asan_poison_memory_region(b.ptr, ChunkSize);
205 int pos = (b.ptr - p) / ChunkSize;
206 flags |= (1ull << pos);
213 [[nodiscard]]
bool is_full() const noexcept {
return flags == 0; }
219 [[nodiscard]]
bool empty() const noexcept {
return flags == uint64_t(-1); }
225 [[nodiscard]]
const char *
data() const noexcept {
return p; }
231 [[nodiscard]]
auto mask() const noexcept {
return flags; }
252 template <
int ChunkSize>
258 std::vector<b_t> bu_vec;
261 typename std::vector<b_t>::iterator bu;
264 [[gnu::noinline]]
void find_non_full_bucket() {
265 bu = std::find_if(bu_vec.begin(), bu_vec.end(), [](
auto const &b) { return !b.is_full(); });
266 if (bu != bu_vec.end())
return;
268 auto pos = std::upper_bound(bu_vec.begin(), bu_vec.end(), b, [](
auto const &b1,
auto const &b2) { return b1.data() < b2.data(); });
269 bu = bu_vec.insert(pos, std::move(b));
298 if ((bu == bu_vec.end()) or (bu->is_full())) find_non_full_bucket();
299 return bu->allocate(s);
311 std::memset(blk.ptr, 0, s);
325 if (bu != bu_vec.end() and bu->owns(b)) {
331 bu = std::lower_bound(bu_vec.begin(), bu_vec.end(), b.ptr, [](
auto const &b1,
auto p) { return b1.data() <= p; });
333 EXPECTS_WITH_MESSAGE((bu != bu_vec.end()),
"Error in nda::mem::multi_bucket::deallocate: Owning bucket not found");
334 EXPECTS_WITH_MESSAGE((bu->owns(b)),
"Error in nda::mem::multi_bucket::deallocate: Owning bucket not found");
338 if (!bu->empty())
return;
339 if (bu_vec.size() <= 1)
return;
348 [[nodiscard]]
bool empty() const noexcept {
return bu_vec.size() == 1 && bu_vec[0].empty(); }
354 [[nodiscard]]
auto const &
buckets() const noexcept {
return bu_vec; }
364 for (
const auto &mb : bu_vec) { res = res || mb.owns(b); }
379 template <
size_t Threshold, Allocator A, Allocator B>
388 static_assert(A::address_space == B::address_space);
415 blk_t allocate(
size_t s)
noexcept {
return s <= Threshold ? small.allocate(s) : big.allocate(s); }
424 blk_t allocate_zero(
size_t s)
noexcept {
return s <= Threshold ? small.allocate_zero(s) : big.allocate_zero(s); }
432 void deallocate(
blk_t b)
noexcept {
return b.s <= Threshold ? small.deallocate(b) : big.deallocate(b); }
440 [[nodiscard]]
bool owns(
blk_t b)
const noexcept {
return small.owns(b) or big.owns(b); }
452 template <Allocator A>
455 long memory_used = 0;
483 std::cerr <<
"Memory leak in allocator: " << memory_used <<
" bytes leaked\n";
496 blk_t b = A::allocate(s);
508 blk_t b = A::allocate_zero(s);
520 if (memory_used < 0) {
522 std::cerr <<
"Memory used by allocator < 0: Memory block to be deleted: b.s = " << b.s <<
", b.ptr = " << (
void *)b.ptr <<
"\n";
533 [[nodiscard]]
bool empty()
const {
return (memory_used == 0); }
541 [[nodiscard]]
bool owns(
blk_t b)
const noexcept {
return A::owns(b); }
559 template <Allocator A>
562 std::vector<uint64_t> hist = std::vector<uint64_t>(65, 0);
598 ++hist[__builtin_clzl(s)];
599 return A::allocate(s);
610 ++hist[__builtin_clzl(s)];
611 return A::allocate_zero(s);
626 [[nodiscard]]
bool owns(
blk_t b)
const noexcept {
return A::owns(b); }
632 [[nodiscard]]
auto const &
histogram() const noexcept {
return hist; }
639 os <<
"Allocation size histogram :\n";
640 os <<
"[0, 2^0): " << hist.back() <<
"\n";
641 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 memcpy and memcpy2D 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.