xo-alloc: try to make commit happen at start of GC cycle
This commit is contained in:
parent
0b0ae07fb8
commit
26f460a60f
4 changed files with 77 additions and 48 deletions
|
|
@ -54,13 +54,15 @@ namespace xo {
|
|||
return std::make_pair(lo_, free_ptr_);
|
||||
}
|
||||
|
||||
/** Reset to empty state **/
|
||||
void reset(std::size_t /*z_ignored*/) { this->clear(); }
|
||||
/** Reset to empty state; provision at least @p need_z bytes of (committed) space **/
|
||||
void reset(std::size_t need_z);
|
||||
|
||||
void capture_object_statistics(capture_phase phase,
|
||||
ObjectStatistics * p_dest) const;
|
||||
|
||||
/** expand available (i.e. committed) space to size @p z **/
|
||||
/** expand available (i.e. committed) space to size at least @p z
|
||||
* In practice will round up to a multiple of @ref page_z_
|
||||
**/
|
||||
bool expand(std::size_t z);
|
||||
|
||||
// inherited from IAlloc...
|
||||
|
|
|
|||
|
|
@ -33,12 +33,16 @@ namespace xo {
|
|||
struct Config {
|
||||
/** initial size in bytes for youngest (Nursery) generation.
|
||||
* GC allocates two nursery spaces of this size.
|
||||
* Will allocate more space as needed
|
||||
* This number represents reserved address space.
|
||||
* pages are committed on demand.
|
||||
* Initial committment will be up to @ref incr_gc_threshold_
|
||||
**/
|
||||
std::size_t initial_nursery_z_ = 0;
|
||||
/** initial size in bytes for oldest (Tenured) generation.
|
||||
* GC allocates two tenured spaces of this size
|
||||
* Will allocate more space as needed
|
||||
* GC allocates two tenured spaces of this size.
|
||||
* This number represents reserved address space.
|
||||
* pages are committed on demand.
|
||||
* Initial committment will be up to @ref full_gc_threshold_
|
||||
**/
|
||||
std::size_t initial_tenured_z_ = 0;
|
||||
/** trigger incremental GC after this many bytes allocated in nursery **/
|
||||
|
|
@ -158,6 +162,8 @@ namespace xo {
|
|||
bool is_gc_enabled() const { return gc_enabled_ == 0; }
|
||||
/** true iff GC has been requested **/
|
||||
bool is_gc_pending() const { return incr_gc_pending_ || full_gc_pending_; }
|
||||
/** true iff full GC pending **/
|
||||
bool is_full_gc_pending() const { return full_gc_pending_; }
|
||||
/** true during (and only during) a GC cycle **/
|
||||
bool gc_in_progress() const { return runstate_.in_progress(); }
|
||||
/** @return reserved size of Nursery to-space **/
|
||||
|
|
@ -170,6 +176,10 @@ namespace xo {
|
|||
std::size_t nursery_after_checkpoint() const;
|
||||
/** @return allocated memory range for nursery **/
|
||||
std::pair<const std::byte *, const std::byte *> nursery_span(role role) const;
|
||||
/** @return nursery bytes used in from-space
|
||||
* (only interesting during GC copy phase, e.g. during scope of a GcCopyCallback call)
|
||||
**/
|
||||
std::size_t nursery_from_allocated() const;
|
||||
/** @return reserved size of Tenured to-space **/
|
||||
std::size_t tenured_to_reserved() const;
|
||||
/** @return committed size of Tenured to-space **/
|
||||
|
|
@ -186,19 +196,19 @@ namespace xo {
|
|||
* and allocated size of that generation
|
||||
* @p role chooses between to-space and from-space
|
||||
**/
|
||||
std::tuple<generation_result, std::size_t, std::size_t> location_of(role role, const void * x) const;
|
||||
std::tuple<generation_result, std::size_t, std::size_t, std::size_t> location_of(role role, const void * x) const;
|
||||
/** @return generation to which object at @p x belongs,
|
||||
* location relative to base address for @p x,
|
||||
* and allocated size of generation
|
||||
**/
|
||||
std::tuple<generation_result,std::size_t,std::size_t> tospace_location_of(const void * x) const;
|
||||
std::tuple<generation_result, std::size_t, std::size_t, std::size_t> tospace_location_of(const void * x) const;
|
||||
/** @return generation that contains @p x, given it's in from-space **/
|
||||
generation_result fromspace_generation_of(const void * x) const;
|
||||
/** @return generation to which object at @p x belongs,
|
||||
* location relative to base address for @p x,
|
||||
* and allocated size of generation
|
||||
**/
|
||||
std::tuple<generation_result,std::size_t,std::size_t> fromspace_location_of(const void * x) const;
|
||||
std::tuple<generation_result, std::size_t, std::size_t, std::size_t> fromspace_location_of(const void * x) const;
|
||||
/** true iff from-space contains @p x **/
|
||||
bool fromspace_contains(const void * x) const;
|
||||
/** @return free pointer for generation @p gen, i.e. nursery or tenured space **/
|
||||
|
|
@ -215,7 +225,10 @@ namespace xo {
|
|||
* Intended for GC visualization.
|
||||
**/
|
||||
CallbackId add_gc_copy_callback(up<GcCopyCallback> fn);
|
||||
/** request garbage collection. **/
|
||||
/** request garbage collection.
|
||||
* If GC currently disabled, collection will be deferred until the next time GC
|
||||
* is in an enabled state. See @ref disable_gc and @ref enable_gc
|
||||
**/
|
||||
void request_gc(generation g);
|
||||
/** disable garbage collection until matching call to @ref enable_gc.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -144,6 +144,12 @@ namespace xo {
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
ArenaAlloc::reset(std::size_t need_z) {
|
||||
this->clear();
|
||||
this->expand(need_z);
|
||||
}
|
||||
|
||||
void
|
||||
ArenaAlloc::capture_object_statistics(capture_phase phase,
|
||||
ObjectStatistics * p_dest) const
|
||||
|
|
|
|||
|
|
@ -68,16 +68,23 @@ namespace xo {
|
|||
|
||||
nursery_[role2int(role::from_space)]
|
||||
= ArenaAlloc::make("NA", nursery_size, config.debug_flag_);
|
||||
|
||||
nursery_[role2int(role::to_space) ]
|
||||
= ArenaAlloc::make("NB", nursery_size, config.debug_flag_);
|
||||
|
||||
tenured_[role2int(role::from_space)]
|
||||
= ArenaAlloc::make("TA", tenured_size, config.debug_flag_);
|
||||
|
||||
tenured_[role2int(role::to_space) ]
|
||||
= ArenaAlloc::make("TB", tenured_size, config.debug_flag_);
|
||||
|
||||
nursery_[role2int(role::from_space)]->expand(config.incr_gc_threshold_);
|
||||
nursery_[role2int(role::to_space) ]->expand(config.incr_gc_threshold_);
|
||||
tenured_[role2int(role::from_space)]->expand(config.full_gc_threshold_);
|
||||
tenured_[role2int(role::to_space) ]->expand(config.full_gc_threshold_);
|
||||
|
||||
mutation_log_[role2int(role::from_space)] = std::make_unique<MutationLog>();
|
||||
mutation_log_[role2int(role::to_space)] = std::make_unique<MutationLog>();
|
||||
mutation_log_[role2int(role::to_space )] = std::make_unique<MutationLog>();
|
||||
defer_mutation_log_ = std::make_unique<MutationLog>();
|
||||
|
||||
this->gc_history_ = CircularBuffer<GcStatisticsHistoryItem>(config.stats_history_z_);
|
||||
|
|
@ -194,6 +201,12 @@ namespace xo {
|
|||
return retval;
|
||||
}
|
||||
|
||||
std::size_t
|
||||
GC::nursery_from_allocated() const
|
||||
{
|
||||
return nursery_from()->allocated();
|
||||
}
|
||||
|
||||
std::size_t
|
||||
GC::nursery_to_reserved() const
|
||||
{
|
||||
|
|
@ -259,7 +272,7 @@ namespace xo {
|
|||
return generation_result::not_found;
|
||||
}
|
||||
|
||||
std::tuple<generation_result, std::size_t, std::size_t>
|
||||
std::tuple<generation_result, std::size_t, std::size_t, std::size_t>
|
||||
GC::location_of(role role, const void *x) const
|
||||
{
|
||||
{
|
||||
|
|
@ -267,7 +280,7 @@ namespace xo {
|
|||
auto [is_tenured, offset] = space->location_of(x);
|
||||
|
||||
if (is_tenured)
|
||||
return std::make_tuple(generation_result::tenured, offset, space->allocated());
|
||||
return std::make_tuple(generation_result::tenured, offset, space->allocated(), space->committed());
|
||||
}
|
||||
|
||||
{
|
||||
|
|
@ -275,19 +288,19 @@ namespace xo {
|
|||
auto [is_nursery, offset] = nursery(role)->location_of(x);
|
||||
|
||||
if (is_nursery)
|
||||
return std::make_tuple(generation_result::nursery, offset, space->allocated());
|
||||
return std::make_tuple(generation_result::nursery, offset, space->allocated(), space->committed());
|
||||
}
|
||||
|
||||
return std::make_tuple(generation_result::not_found, 0, 0);
|
||||
return std::make_tuple(generation_result::not_found, 0, 0, 0);
|
||||
}
|
||||
|
||||
std::tuple<generation_result, std::size_t, std::size_t>
|
||||
std::tuple<generation_result, std::size_t, std::size_t, std::size_t>
|
||||
GC::tospace_location_of(const void * x) const
|
||||
{
|
||||
return location_of(role::to_space, x);
|
||||
}
|
||||
|
||||
std::tuple<generation_result, std::size_t, std::size_t>
|
||||
std::tuple<generation_result, std::size_t, std::size_t, std::size_t>
|
||||
GC::fromspace_location_of(const void * x) const
|
||||
{
|
||||
return location_of(role::from_space, x);
|
||||
|
|
@ -533,29 +546,26 @@ namespace xo {
|
|||
*/
|
||||
std::size_t max_promote_z = nursery_[role2int(role::to_space)]->before_checkpoint();
|
||||
|
||||
log && log(xtag("max_promote_z", max_promote_z));
|
||||
ArenaAlloc * tenured_to = this->tenured_to();
|
||||
|
||||
/* tenured generation may need this much space */
|
||||
std::size_t need_tenured_z = (tenured_to->allocated()
|
||||
+ max_promote_z
|
||||
+ config_.full_gc_threshold_);
|
||||
|
||||
log && log(xtag("alloc_tenured_z", tenured_to->allocated()),
|
||||
xtag("max_promote_z", max_promote_z),
|
||||
xtag("full_gc_threshold", config_.full_gc_threshold_),
|
||||
xtag("need_tenured_z", need_tenured_z));
|
||||
|
||||
tenured_to->expand(tenured_to->allocated()
|
||||
+ max_promote_z
|
||||
+ config_.full_gc_threshold_);
|
||||
|
||||
if (target == generation::tenured) {
|
||||
/* gc on tenured generation may need this much space */
|
||||
std::size_t need_tenured_z = (tenured_[role2int(role::to_space)]->allocated()
|
||||
+ max_promote_z
|
||||
+ config_.full_gc_threshold_);
|
||||
|
||||
log && log("need_tenured_z", need_tenured_z);
|
||||
|
||||
tenured_from()->reset(need_tenured_z);
|
||||
tenured_from()->clear();
|
||||
|
||||
this->swap_tenured();
|
||||
} else {
|
||||
std::size_t avail_tenured_z = tenured_[role2int(role::to_space)]->available();
|
||||
|
||||
log && log(xtag("avail_tenured_z", avail_tenured_z));
|
||||
|
||||
if (avail_tenured_z < max_promote_z) {
|
||||
ArenaAlloc * tenured_to = this->tenured_to();
|
||||
|
||||
tenured_to->expand(max_promote_z);
|
||||
}
|
||||
}
|
||||
|
||||
/* subtracting max_promote_z is correct here, since anything not promoted is garbage */
|
||||
|
|
@ -1220,24 +1230,22 @@ namespace xo {
|
|||
void
|
||||
GC::request_gc(generation target)
|
||||
{
|
||||
/** full collection when >= @ref full_gc_threshold_ bytes added to tenured
|
||||
* generation, since last full collection
|
||||
**/
|
||||
bool need_full_gc
|
||||
= ((target == generation::tenured)
|
||||
|| (this->tenured_to()->after_checkpoint() > config_.full_gc_threshold_)
|
||||
|| !config_.allow_incremental_gc_);
|
||||
|
||||
if (need_full_gc)
|
||||
target = generation::tenured;
|
||||
|
||||
if (!runstate_.in_progress() && (gc_enabled_ == 0)) {
|
||||
if (!config_.allow_incremental_gc_)
|
||||
target = generation::tenured;
|
||||
|
||||
if ((target == generation::nursery)
|
||||
&& (this->tenured_to()->after_checkpoint() > config_.full_gc_threshold_))
|
||||
{
|
||||
/** full collection when >= @ref full_gc_threshold_ bytes added to tenured
|
||||
* generation, since last full collection
|
||||
**/
|
||||
target = generation::tenured;
|
||||
}
|
||||
|
||||
this->execute_gc(target);
|
||||
} else {
|
||||
this->incr_gc_pending_ = true;
|
||||
if (target == generation::tenured)
|
||||
this->full_gc_pending_ = true;
|
||||
this->full_gc_pending_ |= need_full_gc;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue