@@ -47,7 +47,9 @@ Arena::Arena(void *base_in, size_t size_in, size_t alignment_in):
47
47
base(static_cast <char *>(base_in)), end(static_cast <char *>(base_in) + size_in), alignment(alignment_in)
48
48
{
49
49
// Start with one free chunk that covers the entire arena
50
- chunks_free.emplace (base, size_in);
50
+ auto it = size_to_free_chunk.emplace (size_in, base);
51
+ chunks_free.emplace (base, it);
52
+ chunks_free_end.emplace (base + size_in, it);
51
53
}
52
54
53
55
Arena::~Arena ()
@@ -63,26 +65,30 @@ void* Arena::alloc(size_t size)
63
65
if (size == 0 )
64
66
return nullptr ;
65
67
66
- // Pick a large enough free-chunk
67
- auto it = std::find_if (chunks_free.begin (), chunks_free.end (),
68
- [=](const std::map<char *, size_t >::value_type& chunk){ return chunk.second >= size; });
69
- if (it == chunks_free.end ())
68
+ // Pick a large enough free-chunk. Returns an iterator pointing to the first element that is not less than key.
69
+ // This allocation strategy is best-fit. According to "Dynamic Storage Allocation: A Survey and Critical Review",
70
+ // Wilson et. al. 1995, http://www.scs.stanford.edu/14wi-cs140/sched/readings/wilson.pdf, best-fit and first-fit
71
+ // policies seem to work well in practice.
72
+ auto sizePtrIt = size_to_free_chunk.lower_bound (size);
73
+ if (sizePtrIt == size_to_free_chunk.end ())
70
74
return nullptr ;
71
75
72
76
// Create the used-chunk, taking its space from the end of the free-chunk
73
- auto alloced = chunks_used. emplace (it ->first + it-> second - size, size). first ;
74
- if (!(it ->second -= size))
75
- chunks_free .erase (it );
76
- return reinterpret_cast < void *>(alloced ->first );
77
- }
78
-
79
- /* extend the Iterator if other begins at its end */
80
- template < class Iterator , class Pair > bool extend (Iterator it, const Pair& other) {
81
- if (it-> first + it-> second == other. first ) {
82
- it ->second += other. second ;
83
- return true ;
77
+ const size_t sizeRemaining = sizePtrIt ->first - size;
78
+ auto alloced = chunks_used. emplace (sizePtrIt ->second + sizeRemaining, size). first ;
79
+ chunks_free_end .erase (sizePtrIt-> second + sizePtrIt-> first );
80
+ if (sizePtrIt ->first == size) {
81
+ // whole chunk is used up
82
+ chunks_free. erase (sizePtrIt-> second );
83
+ } else {
84
+ // still some memory left in the chunk
85
+ auto itRemaining = size_to_free_chunk. emplace (sizeRemaining, sizePtrIt-> second );
86
+ chunks_free[sizePtrIt ->second ] = itRemaining ;
87
+ chunks_free_end. emplace (sizePtrIt-> second + sizeRemaining, itRemaining) ;
84
88
}
85
- return false ;
89
+ size_to_free_chunk.erase (sizePtrIt);
90
+
91
+ return reinterpret_cast <void *>(alloced->first );
86
92
}
87
93
88
94
void Arena::free (void *ptr)
@@ -97,16 +103,30 @@ void Arena::free(void *ptr)
97
103
if (i == chunks_used.end ()) {
98
104
throw std::runtime_error (" Arena: invalid or double free" );
99
105
}
100
- auto freed = *i;
106
+ std::pair< char *, size_t > freed = *i;
101
107
chunks_used.erase (i);
102
108
103
- // Add space to free map, coalescing contiguous chunks
104
- auto next = chunks_free.upper_bound (freed.first );
105
- auto prev = (next == chunks_free.begin ()) ? chunks_free.end () : std::prev (next);
106
- if (prev == chunks_free.end () || !extend (prev, freed))
107
- prev = chunks_free.emplace_hint (next, freed);
108
- if (next != chunks_free.end () && extend (prev, *next))
109
+ // Coalesc freed with previous chunk
110
+ auto prev = chunks_free_end.find (freed.first );
111
+ if (prev != chunks_free_end.end ()) {
112
+ freed.first -= prev->second ->first ;
113
+ freed.second += prev->second ->first ;
114
+ size_to_free_chunk.erase (prev->second );
115
+ chunks_free_end.erase (prev);
116
+ }
117
+
118
+ // Coalesc freed with chunk after freed
119
+ auto next = chunks_free.find (freed.first + freed.second );
120
+ if (next != chunks_free.end ()) {
121
+ freed.second += next->second ->first ;
122
+ size_to_free_chunk.erase (next->second );
109
123
chunks_free.erase (next);
124
+ }
125
+
126
+ // Add/set space with coalesced free chunk
127
+ auto it = size_to_free_chunk.emplace (freed.second , freed.first );
128
+ chunks_free[freed.first ] = it;
129
+ chunks_free_end[freed.first + freed.second ] = it;
110
130
}
111
131
112
132
Arena::Stats Arena::stats () const
@@ -115,7 +135,7 @@ Arena::Stats Arena::stats() const
115
135
for (const auto & chunk: chunks_used)
116
136
r.used += chunk.second ;
117
137
for (const auto & chunk: chunks_free)
118
- r.free += chunk.second ;
138
+ r.free += chunk.second -> first ;
119
139
r.total = r.used + r.free ;
120
140
return r;
121
141
}
0 commit comments