@@ -90,15 +90,167 @@ namespace alaska {
90
90
}
91
91
92
92
93
- void SizedPage::compact (void ) {}
93
+ // The goal of this function is to take a fragmented heap, and
94
+ // apply a simple two-finger compaction algorithm. We start with
95
+ // a heap that looks like this (# is allocated, _ is free)
96
+ // [###_##_#_#__#_#_#__#]
97
+ // and have pointers to the start and end like this:
98
+ // [###_##_#_#__#_#_#__#]
99
+ // ^ ^
100
+ // We iterate until those pointers are the same. If the left
101
+ // pointer points to an allocated object, we increment it. If the
102
+ // right pointer points to a free object (or a pinned), it is
103
+ // decremented. If neither pointer changes, the left points to a
104
+ // free object and the right to an allocated one, we swap their
105
+ // locations then inc right and dec left.
106
+ //
107
+ // When this process is done you should have a heap that looks
108
+ // like this:
109
+ // [###########_________]
110
+ // The last step in this process is to "reset" the free list to be
111
+ // empty and for "expansion" to begin at the end of the allocated
112
+ // objects. Then, if deemed beneficial, you can use MADV_DONTNEED
113
+ // to free memory back to the kernel.
114
+ //
115
+ // This function then returns how many objects it moved
116
+ long SizedPage::compact (void ) {
117
+ // If there's nothing to do, early return.
118
+ // TODO: threshold this.
119
+ if (live_objects == capacity) return 0 ;
120
+
121
+ // The first step is to clear the free list so that we don't have any
122
+ // corruption problems. Later we will update it to point at the end of
123
+ // the allocated objects.
124
+ allocator.reset_free_list ();
125
+
126
+ // The return value - how many objects this function has moved.
127
+ long moved_objects = 0 ;
128
+
129
+ // A pointer which tracks the last object in the heap. We have this
130
+ // in the event of a pinned object, P:
131
+ // [###########_____P___]
132
+ // ^ last_object points here.
133
+ // If this is not null, whenever we swap an object such that the right
134
+ // pointer points to a free slot, we add that location to the local free
135
+ // list of the allocator.
136
+ // NOTE: this actually is a pointer to the header, not the object.
137
+ Header *last_object = nullptr ;
138
+
139
+ Header *left = headers; // the first object
140
+ Header *right = headers + capacity - 1 ;
141
+
142
+
143
+ auto dump = [&]() {
144
+ if (capacity > 32 ) return ;
145
+ printf (" [" );
146
+ for (int i = 0 ; i < capacity; i++) {
147
+ auto *cur = &headers[i];
148
+
149
+ if (cur->is_free ()) {
150
+ printf (" _" );
151
+ } else {
152
+ printf (" #" );
153
+ }
154
+ }
155
+ printf (" ]\n " );
156
+
157
+ printf (" " );
158
+ for (int i = 0 ; i < capacity; i++) {
159
+ auto *cur = &headers[i];
160
+ if (cur == left or cur == right) {
161
+ printf (" ^" );
162
+ } else if (cur == last_object) {
163
+ printf (" X" );
164
+ } else {
165
+ printf (" " );
166
+ }
167
+ }
168
+ printf (" \n " );
169
+ };
170
+
171
+
172
+ while (right > left) {
173
+ // if left points to an allocated object, we can't do anything so
174
+ // walk it forward (towards the right)
175
+ if (not left->is_free ()) {
176
+ left++;
177
+ continue ;
178
+ }
179
+
180
+ // if the right points to a free slot, we can't do anything so
181
+ // similarly walk it forward (toward the left)
182
+ if (right->is_free ()) {
183
+ // but, if the last object is set, we need to add this to the
184
+ // free list because it constitutes a gap in the heap which
185
+ // would not be allocated in the future by the
186
+ if (last_object != nullptr ) {
187
+ void *free_slot = ind_to_object (header_to_ind (right));
188
+ allocator.release_local (free_slot);
189
+ }
190
+
191
+ right--;
192
+ continue ;
193
+ }
194
+
195
+ auto handle_mapping = right->get_mapping ();
196
+ // At this point, we have the setup we want: the left pointer
197
+ // points to a free slot where the object pointed to by the
198
+ // right pointer can be moved. The one situation that could
199
+ // block us from doing this is if the object we want to move is
200
+ // pinned. If it is we maybe update `last_object` and tick
201
+ // the right pointer.
202
+ if (handle_mapping->is_pinned ()) {
203
+ if (last_object == nullptr ) last_object = right;
204
+ right--;
205
+ continue ;
206
+ }
207
+
208
+
209
+ // Now, we are free to apply the compaction!
210
+ void *object_to_move = ind_to_object (header_to_ind (right));
211
+ void *free_slot = ind_to_object (header_to_ind (left));
212
+
213
+ // Move the memory of the object to the free slot
214
+ memmove (free_slot, object_to_move, object_size);
215
+ // And poison the old location.
216
+ memset (object_to_move, 0xF0 , object_size);
217
+
218
+ // Update the mapping so the handle points to the right location.
219
+ handle_mapping->set_pointer (free_slot);
220
+ // make sure the headers make sense
221
+ *left = *right;
222
+ dump ();
223
+
224
+ ALASKA_ASSERT (left->get_mapping () == right->get_mapping (), " ." );
225
+ right->set_mapping (0 );
226
+ right->size_slack = 0 ;
227
+ moved_objects++;
228
+
229
+ // Note that we don't ight along here. We deal with that on the
230
+ // next iteration of the loop to handle edge cases.
231
+ // left++;
232
+ }
233
+
234
+ dump ();
235
+ // If we were lucky, and no pinned object were found, we need to
236
+ // point last_object to the end of the heap, which at this point
237
+ // is `right`
238
+ if (last_object == nullptr ) last_object = right;
239
+
240
+ // Reset the bump pointer of the free list to right after the
241
+ // last object in the heap.
242
+ void *after_heap = ind_to_object (header_to_ind (last_object + 1 ));
243
+ allocator.reset_bump_allocator (after_heap);
244
+
245
+
246
+ return moved_objects;
247
+ }
94
248
95
249
96
250
97
251
void SizedPage::validate (void ) {}
98
252
99
253
long SizedPage::jumble (void ) {
100
- // printf("Jumble sized page\n");
101
-
102
254
char buf[this ->object_size ]; // BAD
103
255
104
256
// Simple two finger walk to swap every allocation
0 commit comments