History

Minor perf improvements in GC ~ 5%

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.

arr committed 9 years ago

Commit 4697a6a1 parent 2519a932 files · +59 −58Browse files

Unified · Split

2 files
  1. src/vm/garbage_collector.cpp +56 −56
  2. src/vm/garbage_collector.h +3 −2
src/vm/garbage_collector.cpp+56−56
@@ -61,19 +61,6 @@ std::unique_ptr<vm_garbage_collector_t> disvm::runtime::create_no_op_gc(vm_t &) · 60 unmodified lines
6161 return std::make_unique<no_op_gc>();
6262 }
6363
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−
7764 namespace
7865 {
7966 enum class gc_colour_t
@@ -105,6 +92,42 @@ namespace · 25 unmodified lines
10592 {
10693 a->gc_reserved = reinterpret_cast<pointer_t>(c);
10794 }
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;
108131 }
109132
110133 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
123146 set_gc_colour(alloc, get_current_colour(_collection_epoch));
124147
125148 std::lock_guard<std::mutex> lock{ _tracking_allocs_lock };
126− _tracking_allocs.push_back(alloc);
149+ _tracking_allocs.emplace_front(alloc);
127150 }
128151
129152 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
138161
139162 namespace
140163 {
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−
153164 void mark_pointers(pointer_t p, void *cxt)
154165 {
155166 auto a = vm_alloc_t::from_allocation(p);
156167
157168 assert(cxt != nullptr);
158− auto c = reinterpret_cast<mark_cxt_t *>(cxt);
169+ auto c = static_cast<mark_cxt_t *>(cxt);
159170
160171 // Don't examine vm strings or allocations with the current colour
161172 if (c->curr_colour != get_gc_colour(a) && a->alloc_type.get() != c->string_type)
162173 c->push(a);
163174 }
164175
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)
166177 {
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−
173178 // Get roots from threads
174179 for (auto &t : threads)
175180 {
@@ -193,42 +198,32 @@ namespace · 17 unmodified lines
193198 }
194199 }
195200
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>())
198202 disvm::debug::log_msg(component_trace_t::garbage_collector, log_level_t::debug, "gc: roots found: %" PRIuPTR, mark_cxt.size());
199203
200204 // Traverse the graph
201205 while (!mark_cxt.empty())
202206 {
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);
208209
209210 if (curr->alloc_type->map_in_bytes > 0)
210211 enum_pointer_fields(*curr->alloc_type, curr->get_allocation(), mark_pointers, &mark_cxt);
211−
212− set_gc_colour(curr, curr_colour);
213212 }
214213 }
215214
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)
217216 {
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)
222219 {
223220 const auto colour = get_gc_colour(a);
224− if (colour == sweeper_colour)
221+ const auto remove = (colour == sweeper_colour);
222+ if (remove)
225223 dec_ref_count_and_free(a);
226− else
227− tracking_allocs.push_back(a);
228− }
229224
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+ });
232227 }
233228 }
234229
@@ -249,8 +244,13 @@ bool default_garbage_collector_t::collect(std::vector<std::shared_ptr<const vm_t · 14 unmodified lines
249244 disvm::debug::log_msg(component_trace_t::duration, log_level_t::debug, "gc: begin: collect");
250245 }
251246
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");
254254
255255 const auto sweeper_colour = get_sweeper_colour(_collection_epoch);
256256 sweep(_tracking_allocs, sweeper_colour);
src/vm/garbage_collector.h+3−2
@@ -9,7 +9,7 @@ · 8 unmodified lines
99
1010 #include <cstdint>
1111 #include <mutex>
12−#include <deque>
12+#include <forward_list>
1313 #include <vector>
1414 #include <disvm.h>
1515
@@ -37,9 +37,10 @@ namespace disvm · 21 unmodified lines
3737 private:
3838 vm_t &_vm;
3939
40+ void *_mark_cxt;
4041 std::size_t _collection_epoch;
4142 mutable std::mutex _tracking_allocs_lock;
42− std::deque<vm_alloc_t *> _tracking_allocs;
43+ std::forward_list<vm_alloc_t *> _tracking_allocs;
4344 };
4445 }
4546 }