memory_allocator.cpp 4 KB
Newer Older
thatPepe's avatar
thatPepe committed
1
2
3
#include "../allocator.hpp"
#include "../utils.hpp"

4
5
6
7
8
9
10
11
12
13
MemoryPool::MemoryPool(size_t initialSize, size_t alignment)
    : _alignment(alignment) {
    // Validate alignment is power of two
    if ((alignment & (alignment - 1)) != 0 || alignment == 0) {
        throw std::invalid_argument("Alignment must be a power of two");
    }

    if (initialSize > 0) {
        allocateNewRegion(initialSize);
    }
thatPepe's avatar
thatPepe committed
14
15
16
17
18
19
20
21
22
}

MemoryPool::~MemoryPool() {
    for (void *region : _base_regions) {
        RUN_INFINI(infinirtFree(region));
    }
}

void *MemoryPool::alloc(size_t size) {
wooway777's avatar
wooway777 committed
23
24
25
26
    if (size == 0) {
        return nullptr;
    }

27
28
29
30
31
    // Calculate aligned size
    const size_t aligned_size = (size + _alignment - 1) & ~(_alignment - 1);

    // Find the first block with enough space (after alignment)
    auto it = _free_blocks.lower_bound(aligned_size);
thatPepe's avatar
thatPepe committed
32
    if (it == _free_blocks.end()) {
33
34
        allocateNewRegion(aligned_size);
        it = _free_blocks.lower_bound(aligned_size);
thatPepe's avatar
thatPepe committed
35
36
37
38
39
40
41
42
43
44
        if (it == _free_blocks.end()) {
            throw std::bad_alloc();
        }
    }

    auto block_it = it->second;
    Block block = *block_it;
    _free_blocks.erase(it);
    _all_blocks.erase(block_it);

45
    // Align the pointer within the block
wooway777's avatar
wooway777 committed
46
    size_t alignment_padding = reinterpret_cast<char *>(block.ptr) - reinterpret_cast<char *>(block.ptr);
47
48
49
50
51

    // Calculate remaining space after allocation
    const size_t remaining = block.size - aligned_size - alignment_padding;

    // Create allocated block
wooway777's avatar
wooway777 committed
52
    Block alloc_block(block.base, block.ptr, aligned_size, false);
53
    auto alloc_it = _all_blocks.insert(alloc_block).first;
wooway777's avatar
wooway777 committed
54
    _ptr_to_block[block.ptr] = alloc_it;
55
56
57

    // Split remaining space if it's large enough
    if (remaining >= _alignment) {
wooway777's avatar
wooway777 committed
58
        void *rem_ptr = static_cast<char *>(block.ptr) + aligned_size;
59
        Block rem_block(block.base, rem_ptr, remaining, true);
thatPepe's avatar
thatPepe committed
60
        auto rem_it = _all_blocks.insert(rem_block).first;
61
        _free_blocks.emplace(remaining, rem_it);
thatPepe's avatar
thatPepe committed
62
    }
63

wooway777's avatar
wooway777 committed
64
    return block.ptr;
thatPepe's avatar
thatPepe committed
65
66
67
}

void MemoryPool::release(void *ptr) {
wooway777's avatar
wooway777 committed
68
69
70
71
    if (ptr == nullptr) {
        return;
    }

thatPepe's avatar
thatPepe committed
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
    auto it = _ptr_to_block.find(ptr);
    if (it == _ptr_to_block.end()) {
        throw std::runtime_error("Invalid pointer to free");
    }

    auto block_it = it->second;
    Block block = *block_it;
    _all_blocks.erase(block_it);
    block.is_free = true;
    auto new_it = _all_blocks.insert(block).first;
    _ptr_to_block.erase(ptr);
    tryCoalesce(*new_it);
}

void *MemoryPool::allocateNewRegion(size_t size) {
87
    // Allocate exactly the requested size
thatPepe's avatar
thatPepe committed
88
89
90
    void *ptr = nullptr;
    RUN_INFINI(infinirtMalloc(&ptr, size));
    _base_regions.push_back(ptr);
91
92

    // Align the pointer within the allocated region
wooway777's avatar
wooway777 committed
93
    size_t alignment_padding = reinterpret_cast<char *>(ptr) - reinterpret_cast<char *>(ptr);
94
95
    size_t usable_size = size - alignment_padding;

wooway777's avatar
wooway777 committed
96
    Block new_block(ptr, ptr, usable_size, true);
thatPepe's avatar
thatPepe committed
97
    auto it = _all_blocks.insert(new_block).first;
98
99
    _free_blocks.emplace(usable_size, it);

wooway777's avatar
wooway777 committed
100
    return ptr;
thatPepe's avatar
thatPepe committed
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
}

void MemoryPool::tryCoalesce(const Block &block) {
    auto it = _all_blocks.find(block);
    if (it == _all_blocks.end()) {
        return;
    }

    Block merged = *it;
    auto next = std::next(it);
    auto prev = (it == _all_blocks.begin()) ? _all_blocks.end() : std::prev(it);

    _all_blocks.erase(it);
    _free_blocks.erase(merged.size);

    // Coalesce with next
    if (next != _all_blocks.end() && next->is_free && static_cast<char *>(merged.ptr) + merged.size == next->ptr) {
        _free_blocks.erase(next->size);
        merged.size += next->size;
        _all_blocks.erase(next);
    }

    // Coalesce with prev
    if (prev != _all_blocks.end() && prev->is_free && static_cast<char *>(prev->ptr) + prev->size == merged.ptr) {
        _free_blocks.erase(prev->size);
        merged.ptr = prev->ptr;
        merged.size += prev->size;
        merged.base = prev->base;
        _all_blocks.erase(prev);
    }

    merged.is_free = true;
    auto new_it = _all_blocks.insert(merged).first;
    _free_blocks.emplace(merged.size, new_it);
}