Changed allocation collection to a singly linked list - std::forward_list. Moved mark context from stack allocation to heap. - This permits reuse of the allocated memory between mark phases.
Unified · Split
| @@ -61,19 +61,6 @@ std::unique_ptr<vm_garbage_collector_t> disvm::runtime::create_no_op_gc(vm_t &) · 60 unmodified lines | ||
| 61 | 61 | return std::make_unique<no_op_gc>(); |
| 62 | 62 | } |
| 63 | 63 | |
| 64 | −default_garbage_collector_t::default_garbage_collector_t(vm_t &vm) | |
| 65 | − : _collection_epoch{ 0 } | |
| 66 | − , _vm{ vm } | |
| 67 | −{ | |
| 68 | −} | |
| 69 | − | |
| 70 | −default_garbage_collector_t::~default_garbage_collector_t() | |
| 71 | −{ | |
| 72 | − std::lock_guard<std::mutex> lock{ _tracking_allocs_lock }; | |
| 73 | − for (auto a : _tracking_allocs) | |
| 74 | − dec_ref_count_and_free(a); | |
| 75 | −} | |
| 76 | − | |
| 77 | 64 | namespace |
| 78 | 65 | { |
| 79 | 66 | enum class gc_colour_t |
| @@ -105,6 +92,42 @@ namespace · 25 unmodified lines | ||
| 105 | 92 | { |
| 106 | 93 | a->gc_reserved = reinterpret_cast<pointer_t>(c); |
| 107 | 94 | } |
| 95 | + | |
| 96 | + class mark_cxt_t final : public std::stack<vm_alloc_t *, std::vector<vm_alloc_t *>> | |
| 97 | + { | |
| 98 | + public: | |
| 99 | + mark_cxt_t() | |
| 100 | + : string_type{ intrinsic_type_desc::type<vm_string_t>().get() } // Using raw pointer since it is a built-in type and has unlimited lifetime | |
| 101 | + , curr_colour{ gc_colour_t::white } | |
| 102 | + { } | |
| 103 | + | |
| 104 | + const type_descriptor_t *string_type; | |
| 105 | + gc_colour_t curr_colour; | |
| 106 | + | |
| 107 | + vm_alloc_t *pop() | |
| 108 | + { | |
| 109 | + auto a = top(); | |
| 110 | + std::stack<vm_alloc_t *, std::vector<vm_alloc_t *>>::pop(); | |
| 111 | + return a; | |
| 112 | + } | |
| 113 | + }; | |
| 114 | +} | |
| 115 | + | |
| 116 | +default_garbage_collector_t::default_garbage_collector_t(vm_t &vm) | |
| 117 | + : _collection_epoch{ 0 } | |
| 118 | + , _mark_cxt{ new mark_cxt_t{} } | |
| 119 | + , _vm{ vm } | |
| 120 | +{ | |
| 121 | +} | |
| 122 | + | |
| 123 | +default_garbage_collector_t::~default_garbage_collector_t() | |
| 124 | +{ | |
| 125 | + std::lock_guard<std::mutex> lock{ _tracking_allocs_lock }; | |
| 126 | + for (auto a : _tracking_allocs) | |
| 127 | + dec_ref_count_and_free(a); | |
| 128 | + | |
| 129 | + auto cxt = static_cast<mark_cxt_t *>(_mark_cxt); | |
| 130 | + delete cxt; | |
| 108 | 131 | } |
| 109 | 132 | |
| 110 | 133 | vm_memory_allocator_t default_garbage_collector_t::get_allocator() const |
| @@ -123,7 +146,7 @@ void default_garbage_collector_t::track_allocation(vm_alloc_t *alloc) · 12 unmodified lines | ||
| 123 | 146 | set_gc_colour(alloc, get_current_colour(_collection_epoch)); |
| 124 | 147 | |
| 125 | 148 | std::lock_guard<std::mutex> lock{ _tracking_allocs_lock }; |
| 126 | − _tracking_allocs.push_back(alloc); | |
| 149 | + _tracking_allocs.emplace_front(alloc); | |
| 127 | 150 | } |
| 128 | 151 | |
| 129 | 152 | void default_garbage_collector_t::enum_tracked_allocations(vm_alloc_callback_t callback) const |
| @@ -138,38 +161,20 @@ void default_garbage_collector_t::enum_tracked_allocations(vm_alloc_callback_t c · 8 unmodified lines | ||
| 138 | 161 | |
| 139 | 162 | namespace |
| 140 | 163 | { |
| 141 | − struct mark_cxt_t final : public std::stack<vm_alloc_t *, std::vector<vm_alloc_t *>> | |
| 142 | − { | |
| 143 | − mark_cxt_t(const type_descriptor_t *string_type, const gc_colour_t curr_colour) | |
| 144 | − : std::stack<vm_alloc_t *, std::vector<vm_alloc_t *>>{} | |
| 145 | − , string_type{ string_type } | |
| 146 | − , curr_colour{ curr_colour } | |
| 147 | − { } | |
| 148 | − | |
| 149 | − const type_descriptor_t *string_type; | |
| 150 | − const gc_colour_t curr_colour; | |
| 151 | − }; | |
| 152 | − | |
| 153 | 164 | void mark_pointers(pointer_t p, void *cxt) |
| 154 | 165 | { |
| 155 | 166 | auto a = vm_alloc_t::from_allocation(p); |
| 156 | 167 | |
| 157 | 168 | assert(cxt != nullptr); |
| 158 | − auto c = reinterpret_cast<mark_cxt_t *>(cxt); | |
| 169 | + auto c = static_cast<mark_cxt_t *>(cxt); | |
| 159 | 170 | |
| 160 | 171 | // Don't examine vm strings or allocations with the current colour |
| 161 | 172 | if (c->curr_colour != get_gc_colour(a) && a->alloc_type.get() != c->string_type) |
| 162 | 173 | c->push(a); |
| 163 | 174 | } |
| 164 | 175 | |
| 165 | − void mark(const std::vector<std::shared_ptr<const vm_thread_t>> &threads, const gc_colour_t curr_colour) | |
| 176 | + void mark(std::vector<std::shared_ptr<const vm_thread_t>> threads, mark_cxt_t &mark_cxt) | |
| 166 | 177 | { |
| 167 | − mark_cxt_t mark_cxt | |
| 168 | − { | |
| 169 | − intrinsic_type_desc::type<vm_string_t>().get(), // Using raw pointer since it is a built-in type and has unlimited lifetime | |
| 170 | − curr_colour | |
| 171 | − }; | |
| 172 | − | |
| 173 | 178 | // Get roots from threads |
| 174 | 179 | for (auto &t : threads) |
| 175 | 180 | { |
| @@ -193,42 +198,32 @@ namespace · 17 unmodified lines | ||
| 193 | 198 | } |
| 194 | 199 | } |
| 195 | 200 | |
| 196 | − const auto log_enabled = disvm::debug::is_component_tracing_enabled<component_trace_t::garbage_collector>(); | |
| 197 | − if (log_enabled) | |
| 201 | + if (disvm::debug::is_component_tracing_enabled<component_trace_t::garbage_collector>()) | |
| 198 | 202 | disvm::debug::log_msg(component_trace_t::garbage_collector, log_level_t::debug, "gc: roots found: %" PRIuPTR, mark_cxt.size()); |
| 199 | 203 | |
| 200 | 204 | // Traverse the graph |
| 201 | 205 | while (!mark_cxt.empty()) |
| 202 | 206 | { |
| 203 | − auto curr = mark_cxt.top(); | |
| 204 | − mark_cxt.pop(); | |
| 205 | − | |
| 206 | − if (log_enabled) | |
| 207 | − disvm::debug::log_msg(component_trace_t::garbage_collector, log_level_t::debug, "gc: mark: alloc: %#" PRIxPTR, curr); | |
| 207 | + auto curr = mark_cxt.pop(); | |
| 208 | + set_gc_colour(curr, mark_cxt.curr_colour); | |
| 208 | 209 | |
| 209 | 210 | if (curr->alloc_type->map_in_bytes > 0) |
| 210 | 211 | enum_pointer_fields(*curr->alloc_type, curr->get_allocation(), mark_pointers, &mark_cxt); |
| 211 | − | |
| 212 | − set_gc_colour(curr, curr_colour); | |
| 213 | 212 | } |
| 214 | 213 | } |
| 215 | 214 | |
| 216 | − void sweep(std::deque<vm_alloc_t *> &tracking_allocs, const gc_colour_t sweeper_colour) | |
| 215 | + void sweep(std::forward_list<vm_alloc_t *> &tracking_allocs, const gc_colour_t sweeper_colour) | |
| 217 | 216 | { |
| 218 | − auto allocs_local = std::move(tracking_allocs); | |
| 219 | − | |
| 220 | − const auto starting_size = allocs_local.size(); | |
| 221 | − for (auto a : allocs_local) | |
| 217 | + // Remove sweeper colour allocations. | |
| 218 | + tracking_allocs.remove_if([sweeper_colour](vm_alloc_t *a) | |
| 222 | 219 | { |
| 223 | 220 | const auto colour = get_gc_colour(a); |
| 224 | − if (colour == sweeper_colour) | |
| 221 | + const auto remove = (colour == sweeper_colour); | |
| 222 | + if (remove) | |
| 225 | 223 | dec_ref_count_and_free(a); |
| 226 | − else | |
| 227 | − tracking_allocs.push_back(a); | |
| 228 | − } | |
| 229 | 224 | |
| 230 | − if (disvm::debug::is_component_tracing_enabled<component_trace_t::garbage_collector>()) | |
| 231 | − disvm::debug::log_msg(component_trace_t::garbage_collector, log_level_t::debug, "gc: sweep: %u", starting_size - tracking_allocs.size()); | |
| 225 | + return remove; | |
| 226 | + }); | |
| 232 | 227 | } |
| 233 | 228 | } |
| 234 | 229 | |
| @@ -249,8 +244,13 @@ bool default_garbage_collector_t::collect(std::vector<std::shared_ptr<const vm_t · 14 unmodified lines | ||
| 249 | 244 | disvm::debug::log_msg(component_trace_t::duration, log_level_t::debug, "gc: begin: collect"); |
| 250 | 245 | } |
| 251 | 246 | |
| 252 | − const auto curr_colour = get_current_colour(_collection_epoch); | |
| 253 | − mark(threads, curr_colour); | |
| 247 | + auto mark_cxt = static_cast<mark_cxt_t *>(_mark_cxt); | |
| 248 | + assert(mark_cxt != nullptr); | |
| 249 | + mark_cxt->curr_colour = get_current_colour(_collection_epoch); | |
| 250 | + | |
| 251 | + assert(mark_cxt->empty() && "Marking context should be empty before mark phase"); | |
| 252 | + mark(std::move(threads), *mark_cxt); | |
| 253 | + assert(mark_cxt->empty() && "Marking context should be empty after mark phase"); | |
| 254 | 254 | |
| 255 | 255 | const auto sweeper_colour = get_sweeper_colour(_collection_epoch); |
| 256 | 256 | sweep(_tracking_allocs, sweeper_colour); |
| @@ -9,7 +9,7 @@ · 8 unmodified lines | ||
| 9 | 9 | |
| 10 | 10 | #include <cstdint> |
| 11 | 11 | #include <mutex> |
| 12 | −#include <deque> | |
| 12 | +#include <forward_list> | |
| 13 | 13 | #include <vector> |
| 14 | 14 | #include <disvm.h> |
| 15 | 15 | |
| @@ -37,9 +37,10 @@ namespace disvm · 21 unmodified lines | ||
| 37 | 37 | private: |
| 38 | 38 | vm_t &_vm; |
| 39 | 39 | |
| 40 | + void *_mark_cxt; | |
| 40 | 41 | std::size_t _collection_epoch; |
| 41 | 42 | mutable std::mutex _tracking_allocs_lock; |
| 42 | − std::deque<vm_alloc_t *> _tracking_allocs; | |
| 43 | + std::forward_list<vm_alloc_t *> _tracking_allocs; | |
| 43 | 44 | }; |
| 44 | 45 | } |
| 45 | 46 | } |