Accurately track used bytes in Arena
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
#include "arena_allocator.hpp"
|
||||
#include <cassert>
|
||||
#include <iomanip>
|
||||
#include <limits>
|
||||
|
||||
ArenaAllocator::~ArenaAllocator() {
|
||||
while (current_block_) {
|
||||
@@ -12,10 +13,8 @@ ArenaAllocator::~ArenaAllocator() {
|
||||
|
||||
ArenaAllocator::ArenaAllocator(ArenaAllocator &&other) noexcept
|
||||
: initial_block_size_(other.initial_block_size_),
|
||||
current_block_(other.current_block_),
|
||||
current_offset_(other.current_offset_) {
|
||||
current_block_(other.current_block_) {
|
||||
other.current_block_ = nullptr;
|
||||
other.current_offset_ = 0;
|
||||
}
|
||||
|
||||
ArenaAllocator &ArenaAllocator::operator=(ArenaAllocator &&other) noexcept {
|
||||
@@ -28,10 +27,8 @@ ArenaAllocator &ArenaAllocator::operator=(ArenaAllocator &&other) noexcept {
|
||||
|
||||
initial_block_size_ = other.initial_block_size_;
|
||||
current_block_ = other.current_block_;
|
||||
current_offset_ = other.current_offset_;
|
||||
|
||||
other.current_block_ = nullptr;
|
||||
other.current_offset_ = 0;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
@@ -58,15 +55,15 @@ void ArenaAllocator::reset() {
|
||||
// Update first block's counters to reflect only itself
|
||||
if (first_block) {
|
||||
first_block->total_size = first_block->size;
|
||||
first_block->block_count = 1;
|
||||
first_block->total_used = 0;
|
||||
}
|
||||
|
||||
current_block_ = first_block;
|
||||
current_offset_ = 0;
|
||||
current_block_->offset = 0;
|
||||
}
|
||||
|
||||
void *ArenaAllocator::realloc_raw(void *ptr, size_t old_size, size_t new_size,
|
||||
size_t alignment) {
|
||||
void *ArenaAllocator::realloc_raw(void *ptr, uint32_t old_size,
|
||||
uint32_t new_size, uint32_t alignment) {
|
||||
if (ptr == nullptr) {
|
||||
return allocate_raw(new_size, alignment);
|
||||
}
|
||||
@@ -79,14 +76,14 @@ void *ArenaAllocator::realloc_raw(void *ptr, size_t old_size, size_t new_size,
|
||||
assert(current_block_ &&
|
||||
"realloc called with non-null ptr but no current block exists");
|
||||
|
||||
// Assert that current_offset_ is large enough (should always be true for
|
||||
// Assert that offset is large enough (should always be true for
|
||||
// valid callers)
|
||||
assert(current_offset_ >= old_size &&
|
||||
"current_offset_ must be >= old_size for valid last allocation");
|
||||
assert(current_block_->offset >= old_size &&
|
||||
"offset must be >= old_size for valid last allocation");
|
||||
|
||||
// Check if this was the last allocation by comparing with expected location
|
||||
char *expected_last_alloc_start =
|
||||
current_block_->data() + current_offset_ - old_size;
|
||||
current_block_->data() + current_block_->offset - old_size;
|
||||
|
||||
if (ptr == expected_last_alloc_start) {
|
||||
// This is indeed the last allocation
|
||||
@@ -94,15 +91,16 @@ void *ArenaAllocator::realloc_raw(void *ptr, size_t old_size, size_t new_size,
|
||||
// Growing - check if we have space
|
||||
size_t additional_space_needed = new_size - old_size;
|
||||
|
||||
if (current_offset_ + additional_space_needed <= current_block_->size) {
|
||||
if (current_block_->offset + additional_space_needed <=
|
||||
current_block_->size) {
|
||||
// We can extend in place
|
||||
current_offset_ += additional_space_needed;
|
||||
current_block_->offset += additional_space_needed;
|
||||
return ptr;
|
||||
}
|
||||
} else {
|
||||
// Shrinking - just update the offset
|
||||
size_t space_to_free = old_size - new_size;
|
||||
current_offset_ -= space_to_free;
|
||||
current_block_->offset -= space_to_free;
|
||||
return new_size == 0 ? nullptr : ptr;
|
||||
}
|
||||
}
|
||||
@@ -154,14 +152,7 @@ ArenaAllocator::find_intra_arena_pointers() const {
|
||||
uintptr_t block_start = reinterpret_cast<uintptr_t>(b->data());
|
||||
|
||||
// Calculate used bytes in this specific block
|
||||
size_t block_used;
|
||||
if (block_idx == 0) {
|
||||
// Current block - use current_offset_
|
||||
block_used = current_offset_;
|
||||
} else {
|
||||
// Previous blocks are fully used
|
||||
block_used = b->size;
|
||||
}
|
||||
size_t block_used = b->offset;
|
||||
|
||||
uintptr_t block_used_end = block_start + block_used;
|
||||
|
||||
@@ -181,8 +172,8 @@ ArenaAllocator::find_intra_arena_pointers() const {
|
||||
// Calculate used bytes in this specific block
|
||||
size_t block_used;
|
||||
if (block_idx == 0) {
|
||||
// Current block - use current_offset_
|
||||
block_used = current_offset_;
|
||||
// Current block - use offset
|
||||
block_used = current_block_->offset;
|
||||
} else {
|
||||
// Previous blocks are fully used
|
||||
block_used = b->size;
|
||||
@@ -241,8 +232,8 @@ ArenaAllocator::find_address_location(const void *addr) const {
|
||||
// Calculate used bytes in this specific block
|
||||
size_t block_used;
|
||||
if (block_idx == 0) {
|
||||
// Current block - use current_offset_
|
||||
block_used = current_offset_;
|
||||
// Current block - use offset
|
||||
block_used = current_block_->offset;
|
||||
} else {
|
||||
// Previous blocks are fully used
|
||||
block_used = b->size;
|
||||
@@ -273,9 +264,17 @@ void ArenaAllocator::debug_dump(std::ostream &out, bool show_memory_map,
|
||||
return;
|
||||
}
|
||||
|
||||
// Build list of blocks from current to first
|
||||
std::vector<Block *> blocks;
|
||||
Block *block = current_block_;
|
||||
while (block) {
|
||||
blocks.push_back(block);
|
||||
block = block->prev;
|
||||
}
|
||||
|
||||
// Overall statistics
|
||||
size_t used = this->used_bytes();
|
||||
size_t total_alloc = this->total_allocated();
|
||||
size_t used = used_bytes();
|
||||
double utilization = total_alloc > 0 ? (100.0 * used / total_alloc) : 0.0;
|
||||
|
||||
out << "Total allocated: " << total_alloc << " bytes across " << num_blocks()
|
||||
@@ -286,14 +285,6 @@ void ArenaAllocator::debug_dump(std::ostream &out, bool show_memory_map,
|
||||
<< std::endl;
|
||||
out << std::endl;
|
||||
|
||||
// Build list of blocks from current to first
|
||||
std::vector<Block *> blocks;
|
||||
Block *block = current_block_;
|
||||
while (block) {
|
||||
blocks.push_back(block);
|
||||
block = block->prev;
|
||||
}
|
||||
|
||||
out << "Block Chain (newest to oldest):" << std::endl;
|
||||
|
||||
// Display blocks in reverse order (current first)
|
||||
@@ -301,14 +292,7 @@ void ArenaAllocator::debug_dump(std::ostream &out, bool show_memory_map,
|
||||
Block *b = blocks[i];
|
||||
|
||||
// Calculate used bytes in this specific block
|
||||
size_t block_used;
|
||||
if (i == 0) {
|
||||
// Current block - use current_offset_
|
||||
block_used = current_offset_;
|
||||
} else {
|
||||
// Previous blocks are fully used
|
||||
block_used = b->size;
|
||||
}
|
||||
size_t block_used = b->offset;
|
||||
|
||||
double block_util = b->size > 0 ? (100.0 * block_used / b->size) : 0.0;
|
||||
|
||||
@@ -365,8 +349,8 @@ void ArenaAllocator::debug_dump(std::ostream &out, bool show_memory_map,
|
||||
// Calculate used bytes in this specific block
|
||||
size_t block_used;
|
||||
if (i == 0) {
|
||||
// Current block - use current_offset_
|
||||
block_used = current_offset_;
|
||||
// Current block - use offset
|
||||
block_used = current_block_->offset;
|
||||
} else {
|
||||
// Previous blocks are fully used
|
||||
block_used = b->size;
|
||||
@@ -396,13 +380,12 @@ void ArenaAllocator::debug_dump(std::ostream &out, bool show_memory_map,
|
||||
void ArenaAllocator::add_block(size_t size) {
|
||||
Block *new_block = Block::create(size, current_block_);
|
||||
current_block_ = new_block;
|
||||
current_offset_ = 0;
|
||||
}
|
||||
|
||||
size_t ArenaAllocator::calculate_next_block_size(size_t required_size) const {
|
||||
size_t current_size =
|
||||
current_block_ ? current_block_->size : initial_block_size_;
|
||||
size_t doubled_size = current_size * 2;
|
||||
size_t doubled_size = total_allocated() * 2;
|
||||
doubled_size =
|
||||
std::min<size_t>(doubled_size, std::numeric_limits<uint32_t>::max());
|
||||
|
||||
return std::max(required_size, doubled_size);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user