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 | return std::make_unique<no_op_gc>(); | 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 | namespace | 64 | namespace |
| 78 | { | 65 | { |
| 79 | enum class gc_colour_t | 66 | enum class gc_colour_t |
| @@ -105,6 +92,42 @@ namespace · 25 unmodified lines | |||
| 105 | { | 92 | { |
| 106 | a->gc_reserved = reinterpret_cast<pointer_t>(c); | 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 | vm_memory_allocator_t default_garbage_collector_t::get_allocator() const | 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 | set_gc_colour(alloc, get_current_colour(_collection_epoch)); | 146 | set_gc_colour(alloc, get_current_colour(_collection_epoch)); |
| 124 | 147 | ||
| 125 | std::lock_guard<std::mutex> lock{ _tracking_allocs_lock }; | 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 | void default_garbage_collector_t::enum_tracked_allocations(vm_alloc_callback_t callback) const | 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 | namespace | 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 | void mark_pointers(pointer_t p, void *cxt) | 164 | void mark_pointers(pointer_t p, void *cxt) |
| 154 | { | 165 | { |
| 155 | auto a = vm_alloc_t::from_allocation(p); | 166 | auto a = vm_alloc_t::from_allocation(p); |
| 156 | 167 | ||
| 157 | assert(cxt != nullptr); | 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 | // Don't examine vm strings or allocations with the current colour | 171 | // Don't examine vm strings or allocations with the current colour |
| 161 | if (c->curr_colour != get_gc_colour(a) && a->alloc_type.get() != c->string_type) | 172 | if (c->curr_colour != get_gc_colour(a) && a->alloc_type.get() != c->string_type) |
| 162 | c->push(a); | 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 | // Get roots from threads | 178 | // Get roots from threads |
| 174 | for (auto &t : threads) | 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>(); | 201 | if (disvm::debug::is_component_tracing_enabled<component_trace_t::garbage_collector>()) |
| 197 | if (log_enabled) | ||
| 198 | disvm::debug::log_msg(component_trace_t::garbage_collector, log_level_t::debug, "gc: roots found: %" PRIuPTR, mark_cxt.size()); | 202 | disvm::debug::log_msg(component_trace_t::garbage_collector, log_level_t::debug, "gc: roots found: %" PRIuPTR, mark_cxt.size()); |
| 199 | 203 | ||
| 200 | // Traverse the graph | 204 | // Traverse the graph |
| 201 | while (!mark_cxt.empty()) | 205 | while (!mark_cxt.empty()) |
| 202 | { | 206 | { |
| 203 | auto curr = mark_cxt.top(); | 207 | auto curr = mark_cxt.pop(); |
| 204 | mark_cxt.pop(); | 208 | set_gc_colour(curr, mark_cxt.curr_colour); |
| 205 | |||
| 206 | if (log_enabled) | ||
| 207 | disvm::debug::log_msg(component_trace_t::garbage_collector, log_level_t::debug, "gc: mark: alloc: %#" PRIxPTR, curr); | ||
| 208 | 209 | ||
| 209 | if (curr->alloc_type->map_in_bytes > 0) | 210 | if (curr->alloc_type->map_in_bytes > 0) |
| 210 | enum_pointer_fields(*curr->alloc_type, curr->get_allocation(), mark_pointers, &mark_cxt); | 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); | 217 | // Remove sweeper colour allocations. |
| 219 | 218 | tracking_allocs.remove_if([sweeper_colour](vm_alloc_t *a) | |
| 220 | const auto starting_size = allocs_local.size(); | ||
| 221 | for (auto a : allocs_local) | ||
| 222 | { | 219 | { |
| 223 | const auto colour = get_gc_colour(a); | 220 | const auto colour = get_gc_colour(a); |
| 224 | if (colour == sweeper_colour) | 221 | const auto remove = (colour == sweeper_colour); |
| 222 | if (remove) | ||
| 225 | dec_ref_count_and_free(a); | 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>()) | 225 | return remove; |
| 231 | disvm::debug::log_msg(component_trace_t::garbage_collector, log_level_t::debug, "gc: sweep: %u", starting_size - tracking_allocs.size()); | 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 | disvm::debug::log_msg(component_trace_t::duration, log_level_t::debug, "gc: begin: collect"); | 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); | 247 | auto mark_cxt = static_cast<mark_cxt_t *>(_mark_cxt); |
| 253 | mark(threads, curr_colour); | 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 | const auto sweeper_colour = get_sweeper_colour(_collection_epoch); | 255 | const auto sweeper_colour = get_sweeper_colour(_collection_epoch); |
| 256 | sweep(_tracking_allocs, sweeper_colour); | 256 | sweep(_tracking_allocs, sweeper_colour); |
| @@ -9,7 +9,7 @@ · 8 unmodified lines | |||
| 9 | 9 | ||
| 10 | #include <cstdint> | 10 | #include <cstdint> |
| 11 | #include <mutex> | 11 | #include <mutex> |
| 12 | #include <deque> | 12 | #include <forward_list> |
| 13 | #include <vector> | 13 | #include <vector> |
| 14 | #include <disvm.h> | 14 | #include <disvm.h> |
| 15 | 15 | ||
| @@ -37,9 +37,10 @@ namespace disvm · 21 unmodified lines | |||
| 37 | private: | 37 | private: |
| 38 | vm_t &_vm; | 38 | vm_t &_vm; |
| 39 | 39 | ||
| 40 | void *_mark_cxt; | ||
| 40 | std::size_t _collection_epoch; | 41 | std::size_t _collection_epoch; |
| 41 | mutable std::mutex _tracking_allocs_lock; | 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 | } |