xo-gc: move mlog handling to separate tru MutationLogState.*pp
Still entangled with DX1Collector for object spaces
This commit is contained in:
parent
9a3f78d92d
commit
486de5d397
9 changed files with 752 additions and 360 deletions
|
|
@ -65,7 +65,8 @@ namespace xo {
|
|||
|
||||
using size_type = xo::mm::DX1Collector::size_type;
|
||||
|
||||
DX1Collector::DX1Collector(const X1CollectorConfig & cfg) : config_{cfg}
|
||||
DX1Collector::DX1Collector(const X1CollectorConfig & cfg)
|
||||
: config_{cfg}, mlog_state_{cfg.n_generation_, cfg.debug_flag_}
|
||||
{
|
||||
assert(config_.arena_config_.header_.size_bits_ +
|
||||
config_.arena_config_.header_.age_bits_ +
|
||||
|
|
@ -106,6 +107,9 @@ namespace xo {
|
|||
void
|
||||
DX1Collector::_init_mlogs(const X1CollectorConfig & cfg, std::size_t page_z)
|
||||
{
|
||||
this->mlog_state_.init_mlogs(cfg, page_z);
|
||||
|
||||
#ifdef MOVED
|
||||
for (uint32_t igen = 0, ngen = cfg.n_generation_; igen + 1 < ngen; ++igen) {
|
||||
// special case: no use for mutation log for youngest generation,
|
||||
// so don't trouble to allocate one
|
||||
|
|
@ -132,8 +136,10 @@ namespace xo {
|
|||
} else {
|
||||
assert(false);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef MOVED
|
||||
auto
|
||||
DX1Collector::_make_mlog(uint32_t igen, char tag_char, size_t mlog_z, size_t page_z) -> MutationLog
|
||||
{
|
||||
|
|
@ -145,6 +151,7 @@ namespace xo {
|
|||
.hugepage_z_ = page_z,
|
||||
.store_header_flag_ = false});
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
DX1Collector::_init_space(const X1CollectorConfig & cfg)
|
||||
|
|
@ -195,11 +202,15 @@ namespace xo {
|
|||
}
|
||||
}
|
||||
|
||||
mlog_state_.visit_pools(visitor);
|
||||
|
||||
#ifdef MOVED
|
||||
for (uint32_t j = 0; j + 1 < config_.n_generation_; ++j) {
|
||||
for (uint32_t i = 0; i < c_n_role + 1; ++i) {
|
||||
mlog_storage_[i][j].visit_pools(visitor);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
@ -299,6 +310,9 @@ namespace xo {
|
|||
size_type
|
||||
DX1Collector::mutation_log_entries() const noexcept
|
||||
{
|
||||
return mlog_state_.mutation_log_entries();
|
||||
|
||||
#ifdef MOVED
|
||||
size_type z = 0;
|
||||
|
||||
for (Generation gj{0}; gj + 1 < config_.n_generation_; ++gj) {
|
||||
|
|
@ -306,6 +320,7 @@ namespace xo {
|
|||
}
|
||||
|
||||
return z;
|
||||
#endif
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
|
@ -767,47 +782,8 @@ namespace xo {
|
|||
}
|
||||
|
||||
// 4. scan mutation logs
|
||||
for (Generation g(0); g + 1 < config_.n_generation_; ++g) {
|
||||
const DArena * space = this->get_space(role::to_space(), g);
|
||||
const DArena * from = this->get_space(role::from_space(), g);
|
||||
|
||||
// mutation log for generation g records *incoming* pointers
|
||||
// from more senior generations; includes objects from *this*
|
||||
// generation that are older (track since source promotes before
|
||||
// destination)
|
||||
//
|
||||
for (const MutationLogEntry & mrecd : *(mlog_[role::to_space()][g])) {
|
||||
// mutation log entries are only valid until the next assignment
|
||||
// at the source location. Superseded entry may now point
|
||||
// somewhere else. The snapshot member must however point
|
||||
// to this generation, since that's preserved as long as the
|
||||
// log entry survives.
|
||||
|
||||
void * orig_data = mrecd.snap().data();
|
||||
void * curr_data = *mrecd.p_data();
|
||||
|
||||
if (orig_data == curr_data) {
|
||||
// live mlog entry must point to to-space
|
||||
|
||||
if (space->contains_allocated(orig_data)) {
|
||||
++verify_stats_.n_mlog_vital_;
|
||||
} else if (from->contains(curr_data)) {
|
||||
// verify failure.
|
||||
++verify_stats_.n_mlog_from_;
|
||||
} else {
|
||||
// verify failure.
|
||||
++verify_stats_.n_mlog_wild_;
|
||||
}
|
||||
} else {
|
||||
// requirements on superseded log entry:
|
||||
// - snapshot refers to to-space
|
||||
//
|
||||
// no requirements on current data, entry is superseded anyway
|
||||
//
|
||||
++verify_stats_.n_mlog_stale_;
|
||||
}
|
||||
}
|
||||
}
|
||||
mlog_state_.verify_ok(this,
|
||||
&(this->verify_stats_));
|
||||
}
|
||||
|
||||
// restore run state at end of verify cycle
|
||||
|
|
@ -957,7 +933,7 @@ namespace xo {
|
|||
log && log("step 4b : [STUB] keep reachable weak pointers");
|
||||
|
||||
log && log("step 5 : cleanup");
|
||||
this->cleanup_phase(upto);
|
||||
this->_cleanup_phase(upto);
|
||||
|
||||
if (config_.sanitize_flag_) {
|
||||
log && log("step 5b : verify");
|
||||
|
|
@ -987,203 +963,18 @@ namespace xo {
|
|||
log && log("swap roles", xtag("g", g));
|
||||
|
||||
std::swap(space_[role::to_space()][g], space_[role::from_space()][g]);
|
||||
std::swap(mlog_[role::to_space()][g], mlog_[role::from_space()][g]);
|
||||
}
|
||||
|
||||
mlog_state_.swap_roles(upto);
|
||||
}
|
||||
|
||||
void
|
||||
DX1Collector::forward_mutation_log(Generation upto)
|
||||
{
|
||||
/** non-zero if at least one object was rescued (from any generation)
|
||||
* by mutation log scan
|
||||
**/
|
||||
std::size_t work = 0;
|
||||
|
||||
do {
|
||||
// on 1st iteration, for all generations:
|
||||
// - to_mlog, triage_mlog are empty
|
||||
|
||||
for (Generation child_gen{0}; child_gen + 2 < config_.n_generation_; ++child_gen) {
|
||||
|
||||
MutationLog * from_mlog = this->mlog_[role::from_space()][child_gen];
|
||||
|
||||
if (!from_mlog->empty()) {
|
||||
MutationLog * to_mlog = this->mlog_[role::to_space()][child_gen];
|
||||
MutationLog * triage_mlog = this->mlog_[c_n_role][child_gen];
|
||||
|
||||
auto stats = this->_forward_mutation_log_phase(upto,
|
||||
child_gen,
|
||||
from_mlog,
|
||||
to_mlog,
|
||||
triage_mlog);
|
||||
|
||||
from_mlog->clear();
|
||||
|
||||
// {from_mlog, triage_mlog} reverse roles
|
||||
|
||||
std::swap(this->mlog_[role::from_space()][child_gen],
|
||||
this->mlog_[c_n_role][child_gen]);
|
||||
|
||||
work += stats.n_rescue_;
|
||||
}
|
||||
}
|
||||
} while (work > 0);
|
||||
|
||||
// here: reached fixpoints, any remaining triaged mlogs can be discarded
|
||||
for (Generation child_gen{0}; child_gen + 2 < config_.n_generation_; ++child_gen) {
|
||||
MutationLog * triage_mlog = this->mlog_[c_n_role][child_gen];
|
||||
|
||||
triage_mlog->clear();
|
||||
}
|
||||
}
|
||||
|
||||
MutationLogStatistics
|
||||
DX1Collector::_forward_mutation_log_phase(Generation upto,
|
||||
Generation child_gen,
|
||||
MutationLog * from_mlog,
|
||||
MutationLog * keep_mlog,
|
||||
MutationLog * triage_mlog)
|
||||
{
|
||||
scope log(XO_DEBUG(config_.debug_flag_),
|
||||
xtag("child_gen", child_gen),
|
||||
xtag("mlog.size", from_mlog->size()));
|
||||
|
||||
/* categorize each mlog entry based on combination of {src, dest}.
|
||||
* In each case we care about {gen, age} of {src, dest}
|
||||
* objects.
|
||||
*
|
||||
* Enough cases to deserve a table:
|
||||
*
|
||||
* Legend:
|
||||
* - P : parent object
|
||||
* - P' : parent object after this gc phase
|
||||
* - g(P) : generation of parent P.
|
||||
* '+' if gen > child_gen (parent gen not collected this cycle)
|
||||
* - age(P) : age of parent P.
|
||||
*
|
||||
* - C : child object
|
||||
* - C' : child object after this gc phase
|
||||
* - g(C) : generation of child C.
|
||||
* - age(C) : age of child C.
|
||||
*
|
||||
* - 0 : *from_mlog targets this object's generation.
|
||||
* object not eligible for promotion.
|
||||
* Write self* for objects eligible that promote
|
||||
* if they survive this gc cycle.
|
||||
* Write + for 'any generation senior to target'
|
||||
* - 1 : *from_mlog target this object's generation;
|
||||
* object promotes if it survives
|
||||
*
|
||||
* - role : 'to' this phase evacuated
|
||||
* (or in generation not eligible for collection)
|
||||
* 'fr' otherwise
|
||||
*
|
||||
* | mlog | par | | | mlog | upd
|
||||
* case | cur | g(P) | P C | C' | action | P | move
|
||||
* -------+------+-------+--------------+------+---------+--------+-----
|
||||
* MLOG0 | no | | | | discard | | -
|
||||
* | | | | | | |
|
||||
* MLOG1 | yes | * | to:+ fwd:* | to | keep | P->C' | -
|
||||
* MLOG2 | yes | | fr:0 | to | keep | P->C' | C->to
|
||||
* MLOG3 | yes | * | fwd:* - | to | update | P'->C' | -
|
||||
* MLOG4 | yes | | fr:* - | - | triage | - | -
|
||||
|
||||
* notes:
|
||||
* MLOG1 : child C already forwarded (whether or not promoted)
|
||||
* MLOG2 : child C survives (and perhaps promoted).
|
||||
* kept alive by parent in more-senior generation
|
||||
* MLOG3 : parent has been forwarded.
|
||||
* update mlog entry for new parent location
|
||||
* MLOG4 : parent provisionally garbage. triage mlog entry until
|
||||
* definite outcome.
|
||||
*/
|
||||
|
||||
MutationLogStatistics counters;
|
||||
// index of current mlog entry during evac
|
||||
std::uint32_t i_from = 0;
|
||||
|
||||
for (MutationLogEntry & from_entry : *from_mlog) {
|
||||
if (log) {
|
||||
log(xtag("i_from", i_from));
|
||||
}
|
||||
|
||||
if (from_entry.is_superseded()) {
|
||||
// there must be a second mlog entry that refers to
|
||||
// the new child. Rely on that second entry,
|
||||
// skipping this one.
|
||||
|
||||
// [MLOG0] obsolete mutation -> skip
|
||||
++counters.n_stale_;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* here: mlog current */
|
||||
|
||||
Generation parent_gen_to = this->generation_of(role::to_space(),
|
||||
from_entry.parent());
|
||||
|
||||
if (parent_gen_to.is_sentinel()) {
|
||||
void * parent_fr = *from_entry.p_data();
|
||||
|
||||
AllocInfo parent_info = this->alloc_info((std::byte *)parent_fr);
|
||||
|
||||
if (parent_info.is_forwarding_tseq()) {
|
||||
/* [MLOG3] */
|
||||
|
||||
++counters.n_live_parent_;
|
||||
|
||||
// new parent location in to-space
|
||||
// TODO: method on AllocInfo to streamline this
|
||||
void * parent_to = *(void **)parent_fr;
|
||||
|
||||
parent_gen_to = this->generation_of(role::to_space(),
|
||||
parent_to);
|
||||
parent_info = this->alloc_info((std::byte *)parent_to);
|
||||
|
||||
assert(!parent_gen_to.sentinel());
|
||||
|
||||
// Since parent already forwarded, we don't have to preserve child
|
||||
// or update parent object.
|
||||
//
|
||||
// Do need to replace mlog entry to reflect new parent location.
|
||||
|
||||
std::size_t offset
|
||||
= ((std::byte *)from_entry.p_data()
|
||||
- (std::byte *)from_entry.parent());
|
||||
|
||||
void ** p_data_to = (void **)((std::byte *)(parent_to) + offset);
|
||||
void * child_to = *p_data_to;
|
||||
|
||||
MutationLogEntry to_entry(parent_to, p_data_to, from_entry.snap());
|
||||
|
||||
this->_check_keep_mutation_aux(to_entry,
|
||||
parent_gen_to,
|
||||
child_to,
|
||||
keep_mlog);
|
||||
|
||||
|
||||
} else {
|
||||
++counters.n_triage_;
|
||||
|
||||
// parent hasn't been collected and may be garbage.
|
||||
// However this is only provisional, since
|
||||
// parent could turn out to be reachable via some other mutation.
|
||||
|
||||
triage_mlog->push_back(from_entry);
|
||||
}
|
||||
} else {
|
||||
/* [MLOG1, MLOG2] */
|
||||
|
||||
counters += this->_preserve_child_of_live_parent(upto,
|
||||
parent_gen_to,
|
||||
from_entry,
|
||||
keep_mlog);
|
||||
}
|
||||
}
|
||||
|
||||
return counters;
|
||||
mlog_state_.forward_mutation_log(this, upto);
|
||||
}
|
||||
|
||||
#ifdef MOVED
|
||||
MutationLogStatistics
|
||||
DX1Collector::_preserve_child_of_live_parent(Generation upto,
|
||||
Generation parent_gen,
|
||||
|
|
@ -1233,7 +1024,9 @@ namespace xo {
|
|||
|
||||
return counters;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef MOVED
|
||||
bool
|
||||
DX1Collector::_check_keep_mutation_aux(const MutationLogEntry & from_entry,
|
||||
Generation parent_gen_to,
|
||||
|
|
@ -1265,9 +1058,10 @@ namespace xo {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
DX1Collector::cleanup_phase(Generation upto)
|
||||
DX1Collector::_cleanup_phase(Generation upto)
|
||||
{
|
||||
scope log(XO_DEBUG(true), xtag("upto", upto));
|
||||
|
||||
|
|
@ -1328,7 +1122,7 @@ namespace xo {
|
|||
}
|
||||
|
||||
void *
|
||||
DX1Collector::_deep_move_interior(void * from_src,
|
||||
DX1Collector::deep_move_interior(void * from_src,
|
||||
Generation upto)
|
||||
{
|
||||
scope log(XO_DEBUG(config_.debug_flag_));
|
||||
|
|
@ -1948,16 +1742,10 @@ namespace xo {
|
|||
|
||||
// control here: we have an older->younger pointer, need to log it
|
||||
|
||||
// mlog keyed by generation in which pointer _destination_ resides:
|
||||
// collection that moves destination generation around needs to also
|
||||
// update pointers such as this one
|
||||
//
|
||||
MutationLog * mlog = this->mlog_[role::to_space()][dest_g];
|
||||
void ** lhs_addr = reinterpret_cast<void **>(&(p_lhs->data_));
|
||||
|
||||
mlog->push_back(MutationLogEntry(parent,
|
||||
reinterpret_cast<void **>(&(p_lhs->data_)),
|
||||
rhs));
|
||||
}
|
||||
mlog_state_.append_mutation(dest_g, parent, lhs_addr, rhs);
|
||||
} /*assign_member*/
|
||||
|
||||
DX1CollectorIterator
|
||||
DX1Collector::begin() const noexcept
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue