From ae9e97acc2859bae42cd2d62a0345ae221805bd9 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Thu, 2 Apr 2026 21:23:46 -0400 Subject: [PATCH] xo-gc: refactor, focus on DX1Collector+MutationLogState --- include/xo/gc/DX1Collector.hpp | 2 ++ include/xo/gc/GCObjectStore.hpp | 21 ++++----------- include/xo/gc/GCObjectStoreConfig.hpp | 39 +++++++++++++++++++++++++++ include/xo/gc/MutationLogConfig.hpp | 20 ++++++++++++++ include/xo/gc/MutationLogState.hpp | 2 +- include/xo/gc/X1CollectorConfig.hpp | 24 ++++++++++++----- src/gc/CMakeLists.txt | 5 +++- src/gc/DX1Collector.cpp | 8 ++---- src/gc/GCObjectStore.cpp | 39 +++++++++++++-------------- src/gc/GCObjectStoreConfig.cpp | 22 +++++++++++++++ src/gc/MutationLogConfig.cpp | 2 ++ src/gc/MutationLogState.cpp | 13 ++++----- 12 files changed, 140 insertions(+), 57 deletions(-) create mode 100644 include/xo/gc/GCObjectStoreConfig.hpp create mode 100644 src/gc/GCObjectStoreConfig.cpp diff --git a/include/xo/gc/DX1Collector.hpp b/include/xo/gc/DX1Collector.hpp index 6a53b2b..2addb90 100644 --- a/include/xo/gc/DX1Collector.hpp +++ b/include/xo/gc/DX1Collector.hpp @@ -161,6 +161,8 @@ namespace xo { // ----- access methods ----- const X1CollectorConfig & config() const noexcept { return config_; } + const GCObjectStore & gco_store() const noexcept { return gco_store_; } + std::string_view name() const noexcept { return config_.name_; } GCRunState runstate() const noexcept { return runstate_; } const ObjectTypeTable * get_object_types() const noexcept { return &object_types_; } diff --git a/include/xo/gc/GCObjectStore.hpp b/include/xo/gc/GCObjectStore.hpp index 44dc579..18dbee8 100644 --- a/include/xo/gc/GCObjectStore.hpp +++ b/include/xo/gc/GCObjectStore.hpp @@ -5,11 +5,11 @@ #pragma once +#include "GCObjectStoreConfig.hpp" #include "generation.hpp" #include "object_age.hpp" #include -#include -#include +//#include #include namespace xo { @@ -24,7 +24,7 @@ namespace xo { using size_type = DArena::size_type; public: - GCObjectStore(const ArenaConfig & arena_cfg, uint32_t ngen, bool debug_flag); + explicit GCObjectStore(const GCObjectStoreConfig & cfg); const DArena * get_space(role r, Generation g) const noexcept { return space_[r][g]; } DArena * get_space(role r, Generation g) noexcept { return space_[r][g]; } @@ -70,19 +70,8 @@ namespace xo { void _init_space(); private: - /** Configuration for collector spaces. - * Will have (2 x G) of these, - * where G is @ref n_generation_. - * Not using name_ member. - * - * REQUIRE: - * - arena_config_.store_header_flag_ must be true - **/ - ArenaConfig arena_config_; - /** number of generations in use. Same as @ref X1CollectorConfig::n_generation_ **/ - uint32_t n_generation_ = 0; - /** true to enable debug logging **/ - bool debug_flag_ = false; + /** configuration for gc-aware object store **/ + GCObjectStoreConfig config_; /** arena objects for collector managed memory * 1:1 with roles, but polarity reverses for each collection diff --git a/include/xo/gc/GCObjectStoreConfig.hpp b/include/xo/gc/GCObjectStoreConfig.hpp new file mode 100644 index 0000000..40e4483 --- /dev/null +++ b/include/xo/gc/GCObjectStoreConfig.hpp @@ -0,0 +1,39 @@ +/** @file GCObjectStoreConfig.hpp + * + * @author Roland Conybeare, Apr 2026 + **/ + +#pragma once + +#include + +namespace xo { + namespace mm { + + /** @brief record GCObjectStore configuration **/ + class GCObjectStoreConfig { + public: + GCObjectStoreConfig(const ArenaConfig & arena_cfg, + std::uint32_t ngen, + bool debug_flag); + + public: + /** Configuration for collector spaces. + * Will have (2 x G) of these, + * where G is @ref n_generation_. + * Not using name_ member. + * + * REQUIRE: + * - arena_config_.store_header_flag_ must be true + **/ + ArenaConfig arena_config_; + /** number of generations in use. Same as @ref X1CollectorConfig::n_generation_ **/ + std::uint32_t n_generation_ = 0; + /** true to enable debug logging **/ + bool debug_flag_ = false; + }; + + } /*namespace mm*/ +} /*namespace xo*/ + +/* end GCObjectStoreConfig.hpp */ diff --git a/include/xo/gc/MutationLogConfig.hpp b/include/xo/gc/MutationLogConfig.hpp index c8cd37b..89d7041 100644 --- a/include/xo/gc/MutationLogConfig.hpp +++ b/include/xo/gc/MutationLogConfig.hpp @@ -5,24 +5,44 @@ #pragma once +#include "generation.hpp" #include #include namespace xo { namespace mm { + /** @brief configuration for MutationLogState **/ class MutationLogConfig { public: MutationLogConfig(std::uint32_t ngen, + std::uint32_t survive, std::size_t mlog_z, bool debug_flag); + /** age threshold for promotion to generation @p g **/ + uint32_t promotion_threshold(Generation g) const noexcept { + + // TODO: may consider replacing with table-lookup + // Require: if two distinct ages promote to some gen g at the same time, + // then they also promote to gen g+k at the same time for all k>0. + + return g * n_survive_threshold_; + } + + public: /** number of generations in use. * Same as @ref X1CollectorConfig::n_generation_ **/ std::uint32_t n_generation_ = 0; + /** Number of promotion steps. + * An object that survives this number of collections + * advances to the next generation. + **/ + uint32_t n_survive_threshold_ = 2; + /** storage for xgen pointer bookkeeping (aka remembered sets). * Use 3x this value per generation **/ diff --git a/include/xo/gc/MutationLogState.hpp b/include/xo/gc/MutationLogState.hpp index 7ca0d18..0fb5d62 100644 --- a/include/xo/gc/MutationLogState.hpp +++ b/include/xo/gc/MutationLogState.hpp @@ -130,7 +130,7 @@ namespace xo { * helper function to decide whether to keep a mutation log entry * @return true iff mlog entry appended to @p keep_mlog **/ - bool _check_keep_mutation_aux(DX1Collector * gc, + bool _check_keep_mutation_aux(const GCObjectStore & gco_store, const MutationLogEntry & from_entry, Generation parent_gen_to, void * child_to, diff --git a/include/xo/gc/X1CollectorConfig.hpp b/include/xo/gc/X1CollectorConfig.hpp index 1c79579..03a4b11 100644 --- a/include/xo/gc/X1CollectorConfig.hpp +++ b/include/xo/gc/X1CollectorConfig.hpp @@ -5,6 +5,8 @@ #pragma once +#include "GCObjectStoreConfig.hpp" +#include "MutationLogConfig.hpp" #include "object_age.hpp" #include "generation.hpp" #include @@ -36,18 +38,28 @@ namespace xo { **/ X1CollectorConfig with_sanitize_flag(bool x); + /** fetch configuration for gc object store **/ + GCObjectStoreConfig gco_store_config() const noexcept { + return GCObjectStoreConfig(arena_config_, + n_generation_, + debug_flag_); + } + + /** fetch configuration for mutation log store **/ + MutationLogConfig mlog_config() const noexcept { + return MutationLogConfig(n_generation_, + n_survive_threshold_, + mutation_log_z_, + debug_flag_); + } + Generation age2gen(object_age age) const noexcept { return Generation(age % n_survive_threshold_); } /** age threshold for promotion to generation @p g **/ uint32_t promotion_threshold(Generation g) const noexcept { - - // TODO: may consider replacing with table-lookup - // Require: if two distinct ages promote to some gen g at the same time, - // then they also promote to gen g+k at the same time for all k>0. - - return g * n_survive_threshold_; + return mlog_config().promotion_threshold(g); } public: diff --git a/src/gc/CMakeLists.txt b/src/gc/CMakeLists.txt index ec49a41..42ce4ed 100644 --- a/src/gc/CMakeLists.txt +++ b/src/gc/CMakeLists.txt @@ -9,18 +9,21 @@ set(SELF_SRCS IAllocator_DX1Collector.cpp IAllocIterator_DX1CollectorIterator.cpp + X1CollectorConfig.cpp DX1Collector.cpp facet/ICollector_DX1Collector.cpp DX1CollectorIterator.cpp - X1CollectorConfig.cpp + GCObjectStoreConfig.cpp GCObjectStore.cpp MutationLogConfig.cpp MutationLogState.cpp MutationLogEntry.cpp + + ) xo_add_shared_library4(${SELF_LIB} ${PROJECT_NAME}Targets ${PROJECT_VERSION} 1 ${SELF_SRCS}) diff --git a/src/gc/DX1Collector.cpp b/src/gc/DX1Collector.cpp index 091734e..e5045aa 100644 --- a/src/gc/DX1Collector.cpp +++ b/src/gc/DX1Collector.cpp @@ -67,12 +67,8 @@ namespace xo { DX1Collector::DX1Collector(const X1CollectorConfig & cfg) : config_{cfg}, - mlog_state_{ - MutationLogConfig{ - cfg.n_generation_, - cfg.mutation_log_z_, - cfg.debug_flag_}}, - gco_store_{cfg.arena_config_, cfg.n_generation_, cfg.debug_flag_} + mlog_state_{cfg.mlog_config()}, + gco_store_{cfg.gco_store_config()} { assert(config_.arena_config_.header_.size_bits_ + config_.arena_config_.header_.age_bits_ + diff --git a/src/gc/GCObjectStore.cpp b/src/gc/GCObjectStore.cpp index b0948eb..aa71f8b 100644 --- a/src/gc/GCObjectStore.cpp +++ b/src/gc/GCObjectStore.cpp @@ -10,15 +10,12 @@ namespace xo { namespace mm { - GCObjectStore::GCObjectStore(const ArenaConfig & arena_cfg, - uint32_t ngen, bool debug_flag) - : arena_config_{arena_cfg}, - n_generation_{ngen}, - debug_flag_{debug_flag} + GCObjectStore::GCObjectStore(const GCObjectStoreConfig & cfg) + : config_{cfg} { - assert(arena_config_.header_.size_bits_ + - arena_config_.header_.age_bits_ + - arena_config_.header_.tseq_bits_ <= 64); + assert(config_.arena_config_.header_.size_bits_ + + config_.arena_config_.header_.age_bits_ + + config_.arena_config_.header_.tseq_bits_ <= 64); this->_init_space(); } @@ -28,21 +25,21 @@ namespace xo { { assert(c_n_role == 2); - for (uint32_t igen = 0, ngen = n_generation_; igen < ngen; ++igen) { + for (uint32_t igen = 0, ngen = config_.n_generation_; igen < ngen; ++igen) { if (igen < c_max_generation) { { char buf[40]; snprintf(buf, sizeof(buf), "x1-space-G%u-a", igen); this->space_storage_[0][igen] - = DArena::map(arena_config_.with_name(std::string(buf))); + = DArena::map(config_.arena_config_.with_name(std::string(buf))); } { char buf[40]; snprintf(buf, sizeof(buf), "x1-space-G%u-b", igen); this->space_storage_[1][igen] - = DArena::map(arena_config_.with_name(std::string(buf))); + = DArena::map(config_.arena_config_.with_name(std::string(buf))); } this->space_[role::to_space()][igen] = &space_storage_[0][igen]; @@ -52,12 +49,12 @@ namespace xo { } } - for (uint32_t igen = n_generation_; igen < c_max_generation; ++igen) { + for (uint32_t igen = config_.n_generation_; igen < c_max_generation; ++igen) { this->space_[role::to_space()][igen] = nullptr; this->space_[role::from_space()][igen] = nullptr; } - if (n_generation_ == 2) { + if (config_.n_generation_ == 2) { assert(this->get_space(role::to_space(), Generation{2}) == nullptr); } } @@ -65,7 +62,7 @@ namespace xo { Generation GCObjectStore::generation_of(role r, const void * addr) const noexcept { - for (Generation gi{0}; gi < n_generation_; ++gi) { + for (Generation gi{0}; gi < config_.n_generation_; ++gi) { const DArena * arena = this->get_space(r, gi); if (arena->contains(addr)) @@ -78,7 +75,7 @@ namespace xo { auto GCObjectStore::header2size(header_type hdr) const noexcept -> size_type { - uint32_t z = arena_config_.header_.size(hdr); + uint32_t z = config_.arena_config_.header_.size(hdr); return z; } @@ -86,7 +83,7 @@ namespace xo { object_age GCObjectStore::header2age(header_type hdr) const noexcept { - uint32_t age = arena_config_.header_.age(hdr); + uint32_t age = config_.arena_config_.header_.age(hdr); assert(age < c_max_object_age); @@ -96,7 +93,7 @@ namespace xo { uint32_t GCObjectStore::header2tseq(header_type hdr) const noexcept { - uint32_t tseq = arena_config_.header_.tseq(hdr); + uint32_t tseq = config_.arena_config_.header_.tseq(hdr); return tseq; } @@ -105,13 +102,13 @@ namespace xo { GCObjectStore::is_forwarding_header(header_type hdr) const noexcept { /** forwarding pointer encoded as sentinel tseq **/ - return arena_config_.header_.is_forwarding_tseq(hdr); + return config_.arena_config_.header_.is_forwarding_tseq(hdr); } void GCObjectStore::visit_pools(const MemorySizeVisitor & visitor) const { - for (uint32_t j = 0; j < n_generation_; ++j) { + for (uint32_t j = 0; j < config_.n_generation_; ++j) { for (uint32_t i = 0; i < c_n_role; ++i) { space_storage_[i][j].visit_pools(visitor); } @@ -121,7 +118,7 @@ namespace xo { void GCObjectStore::swap_roles(Generation upto) noexcept { - scope log(XO_DEBUG(debug_flag_), xtag("upto", upto)); + scope log(XO_DEBUG(config_.debug_flag_), xtag("upto", upto)); for (Generation g = Generation{0}; g < upto; ++g) { log && log("swap roles", xtag("g", g)); @@ -134,7 +131,7 @@ namespace xo { GCObjectStore::cleanup_phase(Generation upto, bool sanitize_flag) { - scope log(XO_DEBUG(debug_flag_), xtag("upto", upto)); + scope log(XO_DEBUG(config_.debug_flag_), xtag("upto", upto)); // everything live has been copied out of from-space // -> now set to empty diff --git a/src/gc/GCObjectStoreConfig.cpp b/src/gc/GCObjectStoreConfig.cpp new file mode 100644 index 0000000..1d765a8 --- /dev/null +++ b/src/gc/GCObjectStoreConfig.cpp @@ -0,0 +1,22 @@ +/** @file GCObjectStore.cpp + * + * @author Roland Conybeare, Apr 2026 + **/ + +#include "GCObjectStore.hpp" + +namespace xo { + namespace mm { + + GCObjectStoreConfig::GCObjectStoreConfig(const ArenaConfig & arena_cfg, + std::uint32_t ngen, + bool debug_flag) + : arena_config_{arena_cfg}, + n_generation_{ngen}, + debug_flag_{debug_flag} + {} + + } /*namespace mm*/ +} /*namespace xo*/ + +/* end GCObjectStore.cpp */ diff --git a/src/gc/MutationLogConfig.cpp b/src/gc/MutationLogConfig.cpp index 5e2275d..ff2af63 100644 --- a/src/gc/MutationLogConfig.cpp +++ b/src/gc/MutationLogConfig.cpp @@ -9,9 +9,11 @@ namespace xo { namespace mm { MutationLogConfig::MutationLogConfig(std::uint32_t ngen, + std::uint32_t survive, std::size_t mlog_z, bool debug_flag) : n_generation_{ngen}, + n_survive_threshold_{survive}, mutation_log_z_{mlog_z}, debug_flag_{debug_flag} {} diff --git a/src/gc/MutationLogState.cpp b/src/gc/MutationLogState.cpp index ba2d51f..b6198f2 100644 --- a/src/gc/MutationLogState.cpp +++ b/src/gc/MutationLogState.cpp @@ -332,7 +332,7 @@ namespace xo { MutationLogEntry to_entry(parent_to, p_data_to, from_entry.snap()); - this->_check_keep_mutation_aux(gc, + this->_check_keep_mutation_aux(gc->gco_store(), to_entry, parent_gen_to, child_to, @@ -408,25 +408,26 @@ namespace xo { // child_to generation in {gen, gen+1} - this->_check_keep_mutation_aux(gc, from_entry, parent_gen, child_to, keep_mlog); + this->_check_keep_mutation_aux(gc->gco_store(), + from_entry, parent_gen, child_to, keep_mlog); return counters; } bool - MutationLogState::_check_keep_mutation_aux(DX1Collector * gc, + MutationLogState::_check_keep_mutation_aux(const GCObjectStore & gco_store, const MutationLogEntry & from_entry, Generation parent_gen_to, void * child_to, MutationLog * keep_mlog) { Generation child_gen_to - = gc->generation_of(role::to_space(), child_to); + = gco_store.generation_of(role::to_space(), child_to); bool need_mlog_entry = ((child_gen_to + 1 < config_.n_generation_) - && (gc->config().promotion_threshold(parent_gen_to) - > gc->config().promotion_threshold(child_gen_to))); + && (config_.promotion_threshold(parent_gen_to) + > config_.promotion_threshold(child_gen_to))); if (need_mlog_entry) { // 1. P->C pointer is still cross-age (xage), and