From 29cb4c46160d4426fc33b500920cfe5a63806cf1 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Fri, 3 Apr 2026 16:58:25 -0400 Subject: [PATCH] xo-gc: refactor: move .report_object_ages() to GCObjectStore --- include/xo/gc/DX1Collector.hpp | 25 ---- include/xo/gc/GCObjectStore.hpp | 13 ++ src/gc/DX1Collector.cpp | 244 +------------------------------- src/gc/GCObjectStore.cpp | 81 +++++++++++ 4 files changed, 95 insertions(+), 268 deletions(-) diff --git a/include/xo/gc/DX1Collector.hpp b/include/xo/gc/DX1Collector.hpp index ccc2753..514cc3d 100644 --- a/include/xo/gc/DX1Collector.hpp +++ b/include/xo/gc/DX1Collector.hpp @@ -370,31 +370,6 @@ namespace xo { /** cleanup after gc **/ void _cleanup_phase(Generation upto); -#ifdef OBSOLETE - /** move root subgraph at @p from_src to to-space. - * If not in gc-space, visit immediate children and move them. - * Require: runstate_.is_running() - **/ - void * _deep_move_root(obj from_src, Generation upto); - /** Common driver for _deep_move_root(), _deep_move_interior() **/ - void * _deep_move_gc_owned(void * from_src, Generation upto); - /** snap checkpoint containing allocator state - * use to detect forwarding activity after visiting objects - **/ - GCMoveCheckpoint _snap_move_checkpoint(Generation upto); - - /** traverse objects allocated after @p ckp, to make sure their children - * are forwarded. Repeat until traverse doesn't find any unforwarded children - **/ - void _forward_children_until_fixpoint(Generation upto, - const GCMoveCheckpoint & ckp); - - /** Evacuate object at @p *lhs_data to to-space. - * Replace original with forwarding pointer to new location - **/ - void _forward_inplace_aux(AGCObject * lhs_iface, void ** lhs_data, Generation upto); -#endif - /** Verify that pointer {@p iface, @p data} is valid: * destination either in to-space, or somewhere outside this collector **/ diff --git a/include/xo/gc/GCObjectStore.hpp b/include/xo/gc/GCObjectStore.hpp index f10381b..b5be3dd 100644 --- a/include/xo/gc/GCObjectStore.hpp +++ b/include/xo/gc/GCObjectStore.hpp @@ -97,6 +97,19 @@ namespace xo { obj error_mm, obj * p_output) const noexcept; + /** Report per-age-bucket information as an array of dictionaries. + * Scans to-space to count per-age statistics. + * Each dictionary has keys "n-live" and "bytes". + * Array index corresponds to object age. + * + * @p mm allocate stats from this allocator. + * @p error_mm allocator for error reporting when out-of-memory. + * @p p_output on exit @p *p_output contains stats array + **/ + bool report_object_ages(obj mm, + obj error_mm, + obj * p_output) const noexcept; + /** snap checkpoint containing allocator state * use to detect forwarding activity after visiting objects **/ diff --git a/src/gc/DX1Collector.cpp b/src/gc/DX1Collector.cpp index 015492b..3b2eefd 100644 --- a/src/gc/DX1Collector.cpp +++ b/src/gc/DX1Collector.cpp @@ -314,113 +314,6 @@ namespace xo { obj * p_output) const noexcept { return gco_store_.report_object_types(mm, error_mm, p_output); - -#ifdef MOVED - scope log(XO_DEBUG(true)); - - (void)error_mm; - - bool ok = true; - - // stats, indexed by tseq - // could use c++ vector in scratch space instead of running on - // boxed types. - // - DArray * stats_v = DArray::empty(mm, object_types_.size()); - - if (!stats_v) - return false; - - stats_v->resize(stats_v->capacity()); - - log && log(xtag("object_types_.size", object_types_.size()), - xtag("stats_v.capacity", stats_v->capacity()), - xtag("stats_v.size", stats_v->size())); - - // count #of occupied type slots - std::uint32_t n_tseq_present = 0; - // largest tseq present with non-null AGCObject* iface - std::int32_t max_tseq = 0; - - for (const ObjectTypeSlot & slot : object_types_) { - AGCObject * iface = slot.iface(); - - if (iface) { - typeseq tseq = iface->_typeseq(); - - ++n_tseq_present; - if (max_tseq < tseq.seqno()) - max_tseq = tseq.seqno(); - - assert(tseq.seqno() >= 0); - - auto tname_sv = TypeRegistry::id2name(tseq); - DString * tname = DString::from_view(mm, tname_sv); - - DDictionary * recd = DDictionary::make(mm); - - if (!recd) - return false; - - recd->upsert_cstr(mm, "name", obj(tname)); - recd->upsert_cstr(mm, "tseq", DInteger::box(mm, tseq.seqno())); - recd->upsert_cstr(mm, "n-live", DInteger::box(mm, 0)); - recd->upsert_cstr(mm, "bytes", DInteger::box(mm, 0)); - - stats_v->assign_at(tseq.seqno(), obj(recd)); - } - } - - // scan to-space, count objects by type - - for (Generation g{0}; g < config_.n_generation_; ++g) { - const DArena * arena = this->get_space(role::to_space(), g); - - for (AllocInfo info : *arena) { - if (info.is_forwarding_tseq()) { - assert(false); - return false; - } - - uint32_t ix = info.tseq(); - size_t z = info.size(); - - auto recd = obj::from(stats_v->at(ix)); - - assert(recd); - - auto n_live_opt = recd->lookup_cstr("n-live"); - assert(n_live_opt); - auto bytes_opt = recd->lookup_cstr("bytes"); - assert(bytes_opt); - - if (n_live_opt && bytes_opt) { - auto n_live_gco = obj::from(n_live_opt.value()); - auto bytes_gco = obj::from(bytes_opt.value()); - - n_live_gco->assign_value(n_live_gco->value() + 1); - bytes_gco->assign_value(bytes_gco->value() + z); - } - } - } - - stats_v->resize(max_tseq + 1); - - DArray * final_stats_v = DArray::empty(mm, n_tseq_present); - - for (std::size_t i = 0, n = stats_v->size(); i < n; ++i) { - auto recd = stats_v->at(i); - - if (recd) { - bool ok = final_stats_v->push_back(recd); - assert(ok); - } - } - - *p_output = obj(final_stats_v); - - return ok; -#endif } bool @@ -428,82 +321,7 @@ namespace xo { obj error_mm, obj * p_output) const noexcept { - //return gco_store_.report_object_ages(mm, error_mm, p_output); - - scope log(XO_DEBUG(true)); - - (void)error_mm; - - std::uint64_t n_age = config_.arena_config_.header_.max_age() + 1; - - // stats, indexed by age - DArray * stats_v = DArray::empty(mm, n_age); - - if (!stats_v) - return false; - - // pre-populate with empty dictionaries for each age bucket - for (std::uint64_t a = 0; a < n_age; ++a) { - DDictionary * recd = DDictionary::make(mm); - - if (!recd) - return false; - - recd->upsert_cstr(mm, "age", DInteger::box(mm, a)); - recd->upsert_cstr(mm, "n-live", DInteger::box(mm, 0)); - recd->upsert_cstr(mm, "bytes", DInteger::box(mm, 0)); - - stats_v->push_back(obj(recd)); - } - - log && log(xtag("n_age", n_age), - xtag("stats_v.size", stats_v->size())); - - // scan to-space, count objects by age - - // track largest age with at least one object - std::int64_t max_age_present = 0; - - for (Generation g{0}; g < config_.n_generation_; ++g) { - const DArena * arena = this->get_space(role::to_space(), g); - - for (AllocInfo info : *arena) { - if (info.is_forwarding_tseq()) { - assert(false); - return false; - } - - uint32_t age = info.age(); - size_t z = info.size(); - - if (static_cast(age) > max_age_present) - max_age_present = age; - - auto recd = obj::from(stats_v->at(age)); - - assert(recd); - - auto n_live_opt = recd->lookup_cstr("n-live"); - assert(n_live_opt); - auto bytes_opt = recd->lookup_cstr("bytes"); - assert(bytes_opt); - - if (n_live_opt && bytes_opt) { - auto n_live_gco = obj::from(n_live_opt.value()); - auto bytes_gco = obj::from(bytes_opt.value()); - - n_live_gco->assign_value(n_live_gco->value() + 1); - bytes_gco->assign_value(bytes_gco->value() + z); - } - } - } - - // trim to only report ages up to max observed - stats_v->resize(max_age_present + 1); - - *p_output = obj(stats_v); - - return true; + return gco_store_.report_object_types(mm, error_mm, p_output); } size_type @@ -783,54 +601,6 @@ namespace xo { this->runstate_ = GCRunState::idle(); } -#ifdef OBSOLETE - void * - DX1Collector::_deep_move_root(obj from_src, - Generation upto) - { - return gco_store_._deep_move_root(this, from_src, upto); - } -#endif - -#ifdef OBSOLETE - /* - * rules: - * - from_src must be in from-space - * - object type stored in alloc header - * - return value is new location in to-space - * - * - preserving i/face pointer - * - replace destination with forwarding pointer - * - * EDITOR: gc -> self - */ - void * - DX1Collector::_deep_move_gc_owned(void * from_src, - Generation upto) - { - return gco_store_._deep_move_gc_owned(this, from_src, upto); - } /*_deep_move_gc_owned*/ -#endif - -#ifdef OBSOLETE - auto - DX1Collector::_snap_move_checkpoint(Generation upto) -> GCMoveCheckpoint - { - return gco_store_.snap_move_checkpoint(upto); - } -#endif - -#ifdef OSBOLETE - void - DX1Collector::_forward_children_until_fixpoint(Generation upto, - const GCMoveCheckpoint & gray_lo_v) - { - // problem -- need object type lookup - - gco_store_._forward_children_until_fixpoint(this, upto, gray_lo_v); - } -#endif - void DX1Collector::copy_roots(Generation upto) noexcept { @@ -869,18 +639,6 @@ namespace xo { } } -#ifdef OBSOLETE - void - DX1Collector::_forward_inplace_aux(AGCObject * lhs_iface, - void ** lhs_data, - Generation upto) - { - // upto == runstate_.gc_upto() - - gco_store_._forward_inplace_aux(this, lhs_iface, lhs_data, upto); - } /*_forward_inplace_aux*/ -#endif - void DX1Collector::_verify_aux(AGCObject * iface, void * data) { diff --git a/src/gc/GCObjectStore.cpp b/src/gc/GCObjectStore.cpp index 27f70a7..9bbd210 100644 --- a/src/gc/GCObjectStore.cpp +++ b/src/gc/GCObjectStore.cpp @@ -352,6 +352,87 @@ namespace xo { return ok; } + bool + GCObjectStore::report_object_ages(obj mm, + obj error_mm, + obj * p_output) const noexcept + { + scope log(XO_DEBUG(true)); + + (void)error_mm; + + std::uint64_t n_age = config_.arena_config_.header_.max_age() + 1; + + // stats, indexed by age + DArray * stats_v = DArray::empty(mm, n_age); + + if (!stats_v) + return false; + + // pre-populate with empty dictionaries for each age bucket + for (std::uint64_t a = 0; a < n_age; ++a) { + DDictionary * recd = DDictionary::make(mm); + + if (!recd) + return false; + + recd->upsert_cstr(mm, "age", DInteger::box(mm, a)); + recd->upsert_cstr(mm, "n-live", DInteger::box(mm, 0)); + recd->upsert_cstr(mm, "bytes", DInteger::box(mm, 0)); + + stats_v->push_back(obj(recd)); + } + + log && log(xtag("n_age", n_age), + xtag("stats_v.size", stats_v->size())); + + // scan to-space, count objects by age + + // track largest age with at least one object + std::int64_t max_age_present = 0; + + for (Generation g{0}; g < config_.n_generation_; ++g) { + const DArena * arena = this->get_space(role::to_space(), g); + + for (AllocInfo info : *arena) { + if (info.is_forwarding_tseq()) { + assert(false); + return false; + } + + uint32_t age = info.age(); + size_t z = info.size(); + + if (static_cast(age) > max_age_present) + max_age_present = age; + + auto recd = obj::from(stats_v->at(age)); + + assert(recd); + + auto n_live_opt = recd->lookup_cstr("n-live"); + assert(n_live_opt); + auto bytes_opt = recd->lookup_cstr("bytes"); + assert(bytes_opt); + + if (n_live_opt && bytes_opt) { + auto n_live_gco = obj::from(n_live_opt.value()); + auto bytes_gco = obj::from(bytes_opt.value()); + + n_live_gco->assign_value(n_live_gco->value() + 1); + bytes_gco->assign_value(bytes_gco->value() + z); + } + } + } + + // trim to only report ages up to max observed + stats_v->resize(max_age_present + 1); + + *p_output = obj(stats_v); + + return true; + } + bool GCObjectStore::_check_move_policy(header_type alloc_hdr, void * object_data,