xo-alloc / xo-object: utest coverage + assorted bugfixes
This commit is contained in:
parent
8b622e6999
commit
a6e4430825
21 changed files with 861 additions and 284 deletions
|
|
@ -15,36 +15,20 @@ namespace xo {
|
|||
*
|
||||
* @text
|
||||
*
|
||||
* before @ref release_redline_memory
|
||||
*
|
||||
* <-----allocated----> <-free-> <-reserved->
|
||||
* XXXXXXXXXXXXXXXXXXXX______________________
|
||||
* ^ ^ ^ ^
|
||||
* lo free redline hi
|
||||
* limit
|
||||
*
|
||||
* after @ref release_redline_memory
|
||||
*
|
||||
* <-----allocated----> <--------free------->
|
||||
* XXXXXXXXXXXXXXXXXXXX______________________
|
||||
* ^ ^ ^
|
||||
* lo free hi
|
||||
* limit
|
||||
* @endtext
|
||||
*
|
||||
* TODO: rename to ArenaAlloc
|
||||
**/
|
||||
class ArenaAlloc : public IAlloc {
|
||||
public:
|
||||
~ArenaAlloc();
|
||||
|
||||
/** create allocator with capacity @p z,
|
||||
* with reserved capacity @p redline_z.
|
||||
**/
|
||||
static up<ArenaAlloc> make(const std::string & name,
|
||||
#ifdef REDLINE_MEMORY
|
||||
std::size_t redline_z,
|
||||
#endif
|
||||
std::size_t z,
|
||||
bool debug_flag);
|
||||
|
||||
|
|
@ -56,7 +40,7 @@ namespace xo {
|
|||
|
||||
// inherited from IAlloc...
|
||||
|
||||
virtual const std::string & name() const final override { return name_; }
|
||||
virtual const std::string & name() const final override;
|
||||
virtual std::size_t size() const final override;
|
||||
virtual std::size_t available() const final override;
|
||||
virtual std::size_t allocated() const final override;
|
||||
|
|
@ -64,19 +48,14 @@ namespace xo {
|
|||
virtual bool is_before_checkpoint(const void * x) const final override;
|
||||
virtual std::size_t before_checkpoint() const final override;
|
||||
virtual std::size_t after_checkpoint() const final override;
|
||||
virtual bool debug_flag() const final override;
|
||||
|
||||
virtual void clear() final override;
|
||||
virtual void checkpoint() final override;
|
||||
virtual std::byte * alloc(std::size_t z) final override;
|
||||
#ifdef REDLINE_MEMORY
|
||||
virtual void release_redline_memory() final override;
|
||||
#endif
|
||||
|
||||
private:
|
||||
ArenaAlloc(const std::string & name,
|
||||
#ifdef REDLINE_MEMORY
|
||||
std::size_t rz,
|
||||
#endif
|
||||
std::size_t z, bool debug_flag);
|
||||
|
||||
private:
|
||||
|
|
@ -99,10 +78,6 @@ namespace xo {
|
|||
std::byte * free_ptr_ = nullptr;
|
||||
/** soft limit: end of released memory **/
|
||||
std::byte * limit_ = nullptr;
|
||||
#ifdef REDLINE_MEMORY
|
||||
/** amount of last-resort memory to reserve **/
|
||||
std::size_t redline_z_ = 0;
|
||||
#endif
|
||||
/** hard limit: end of allocated memory **/
|
||||
std::byte * hi_ = nullptr;
|
||||
/** true to enable detailed debug logging **/
|
||||
|
|
|
|||
|
|
@ -199,6 +199,15 @@ namespace xo {
|
|||
#endif
|
||||
|
||||
private:
|
||||
ListAlloc * nursery_to() const { return nursery(role::to_space); }
|
||||
ListAlloc * nursery_from() const { return nursery(role::from_space); }
|
||||
|
||||
ListAlloc * tenured_to() const { return tenured(role::to_space); }
|
||||
ListAlloc * tenured_from() const { return tenured(role::from_space); }
|
||||
|
||||
ListAlloc * nursery(role r) const { return nursery_[role2int(r)].get(); }
|
||||
ListAlloc * tenured(role r) const { return tenured_[role2int(r)].get(); }
|
||||
|
||||
/** begin GC now **/
|
||||
void execute_gc(generation g);
|
||||
/** cleanup phase. aux function for @ref execute_gc **/
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ namespace xo {
|
|||
/** number of bytes allocated since @ref checkpoint **/
|
||||
virtual std::size_t after_checkpoint() const = 0;
|
||||
/** @return true iff debug logging enabled **/
|
||||
virtual bool debug_flag() const { return false; }
|
||||
virtual bool debug_flag() const = 0;
|
||||
|
||||
/** reset allocator to empty state. **/
|
||||
virtual void clear() = 0;
|
||||
|
|
@ -76,10 +76,6 @@ namespace xo {
|
|||
* Only used in @ref GC. Default implementation asserts and returns nullptr
|
||||
**/
|
||||
virtual std::byte * alloc_gc_copy(std::size_t z, const void * src);
|
||||
#ifdef REDLINE_MEMORY
|
||||
/** release last-resort reserved memory **/
|
||||
virtual void release_redline_memory() = 0;
|
||||
#endif
|
||||
};
|
||||
} /*namespace gc*/
|
||||
|
||||
|
|
|
|||
|
|
@ -29,9 +29,6 @@ namespace xo {
|
|||
ListAlloc(std::unique_ptr<ArenaAlloc> hd,
|
||||
ArenaAlloc * marked,
|
||||
std::size_t cz, std::size_t nz, std::size_t tz,
|
||||
#ifdef REDLINE_MEMORY
|
||||
bool use_redline,
|
||||
#endif
|
||||
bool debug_flag);
|
||||
~ListAlloc();
|
||||
|
||||
|
|
@ -40,8 +37,8 @@ namespace xo {
|
|||
/** reset to have at least @p z bytes of storage **/
|
||||
bool reset(std::size_t z);
|
||||
|
||||
/** expand bucket list to accomodate a requrest of size @p z **/
|
||||
bool expand(std::size_t z);
|
||||
/** expand bucket list to accomodate a request of size @p z **/
|
||||
bool expand(std::size_t z, const std::string & name);
|
||||
|
||||
/** current free pointer **/
|
||||
std::byte * free_ptr() const;
|
||||
|
|
@ -64,13 +61,11 @@ namespace xo {
|
|||
virtual bool is_before_checkpoint(const void * x) const final override;
|
||||
virtual std::size_t before_checkpoint() const final override;
|
||||
virtual std::size_t after_checkpoint() const final override;
|
||||
virtual bool debug_flag() const final override;
|
||||
|
||||
virtual void clear() final override;
|
||||
virtual void checkpoint() final override;
|
||||
virtual std::byte * alloc(std::size_t z) final override;
|
||||
#ifdef REDLINE_MEMORY
|
||||
virtual void release_redline_memory() final override;
|
||||
#endif
|
||||
|
||||
private:
|
||||
/** **/
|
||||
|
|
@ -89,11 +84,6 @@ namespace xo {
|
|||
std::size_t next_z_ = 0;
|
||||
/** total size of @ref hd_ + contents of @ref full_l_ **/
|
||||
std::size_t total_z_ = 0;
|
||||
#ifdef REDLINE_MEMORY
|
||||
bool use_redline_ = false;
|
||||
bool redlined_flag_ = false;
|
||||
#endif
|
||||
|
||||
/** true to enable debug logging **/
|
||||
bool debug_flag_ = false;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3,6 +3,9 @@
|
|||
* author: Roland Conybeare, Aug 2025
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ostream>
|
||||
#include <cstdint>
|
||||
|
||||
namespace xo {
|
||||
|
|
@ -15,6 +18,13 @@ namespace xo {
|
|||
|
||||
constexpr std::size_t gen2int(generation x) { return static_cast<std::size_t>(x); }
|
||||
|
||||
const char * gen2str(generation x);
|
||||
|
||||
inline std::ostream & operator<<(std::ostream & os, generation x) {
|
||||
os << gen2str(x);
|
||||
return os;
|
||||
}
|
||||
|
||||
enum class generation_result {
|
||||
nursery,
|
||||
tenured,
|
||||
|
|
|
|||
|
|
@ -13,36 +13,19 @@
|
|||
namespace xo {
|
||||
namespace gc {
|
||||
ArenaAlloc::ArenaAlloc(const std::string & name,
|
||||
#ifdef REDLINE_MEMORY
|
||||
std::size_t rz,
|
||||
#endif
|
||||
std::size_t z, bool debug_flag)
|
||||
{
|
||||
this->name_ = name;
|
||||
#ifdef REDLINE_MEMORY
|
||||
this->lo_ = (new std::byte [rz + z]);
|
||||
#else
|
||||
this->lo_ = (new std::byte [z]);
|
||||
#endif
|
||||
this->checkpoint_ = lo_;
|
||||
this->free_ptr_ = lo_;
|
||||
this->limit_ = lo_ + z;
|
||||
#ifdef REDLINE_MEMORY
|
||||
this->redline_z_ = rz;
|
||||
this->hi_ = limit_ + rz;
|
||||
#else
|
||||
this->hi_ = limit_;
|
||||
#endif
|
||||
this->debug_flag_ = debug_flag;
|
||||
|
||||
if (!lo_) {
|
||||
#ifdef REDLINE_MEMORY
|
||||
throw std::runtime_error(tostr("ArenaAlloc: allocation failed",
|
||||
xtag("size", rz + z)));
|
||||
#else
|
||||
throw std::runtime_error(tostr("ArenaAlloc: allocation failed",
|
||||
xtag("size", z)));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -56,24 +39,15 @@ namespace xo {
|
|||
this->checkpoint_ = nullptr;
|
||||
this->free_ptr_ = nullptr;
|
||||
this->limit_ = nullptr;
|
||||
#ifdef REDLINE_MEMORY
|
||||
this->redline_z_ = 0;
|
||||
#endif
|
||||
this->hi_ = nullptr;
|
||||
this->debug_flag_ = false;
|
||||
}
|
||||
|
||||
up<ArenaAlloc>
|
||||
ArenaAlloc::make(const std::string & name,
|
||||
#ifdef REDLINE_MEMORY
|
||||
std::size_t rz,
|
||||
#endif
|
||||
std::size_t z, bool debug_flag)
|
||||
{
|
||||
return up<ArenaAlloc>(new ArenaAlloc(name,
|
||||
#ifdef REDLINE_MEMORY
|
||||
rz,
|
||||
#endif
|
||||
z, debug_flag));
|
||||
}
|
||||
|
||||
|
|
@ -97,22 +71,36 @@ namespace xo {
|
|||
ArenaAlloc::capture_object_statistics(capture_phase phase,
|
||||
ObjectStatistics * p_dest) const
|
||||
{
|
||||
scope log(XO_DEBUG(debug_flag_),
|
||||
xtag("name", name_),
|
||||
xtag("capacity", limit_ - lo_),
|
||||
xtag("alloc", free_ptr_ - lo_),
|
||||
xtag("lo", (void*)lo_),
|
||||
xtag("free_ptr", (void*)free_ptr_));
|
||||
|
||||
using xo::reflect::TaggedPtr;
|
||||
|
||||
std::byte * p = lo_;
|
||||
|
||||
while (p < free_ptr_) {
|
||||
Object * obj = reinterpret_cast<Object *>(p);
|
||||
TaggedPtr tp = obj->self_tp();
|
||||
std::size_t z = obj->_shallow_size();
|
||||
Object * obj = reinterpret_cast<Object *>(p);
|
||||
TaggedPtr tp = obj->self_tp();
|
||||
std::size_t z = obj->_shallow_size();
|
||||
std::uint32_t id = tp.td()->id().id();
|
||||
|
||||
log && log(xtag("obj", (void*)obj),
|
||||
xtag("z", z),
|
||||
xtag("typeid", id));
|
||||
|
||||
if (p_dest->per_type_stats_v_.size() < id + 1)
|
||||
p_dest->per_type_stats_v_.resize(id + 1);
|
||||
|
||||
PerObjectTypeStatistics & dest = p_dest->per_type_stats_v_.at(id);
|
||||
|
||||
dest.td_ = tp.td();
|
||||
|
||||
log && log(xtag("td", tp.td()->short_name()));
|
||||
|
||||
switch (phase) {
|
||||
case capture_phase::sab:
|
||||
++dest.scanned_n_;
|
||||
|
|
@ -130,6 +118,11 @@ namespace xo {
|
|||
assert(p == free_ptr_);
|
||||
}
|
||||
|
||||
const std::string &
|
||||
ArenaAlloc::name() const {
|
||||
return name_;
|
||||
}
|
||||
|
||||
std::size_t
|
||||
ArenaAlloc::size() const {
|
||||
return limit_ - lo_;
|
||||
|
|
@ -167,15 +160,17 @@ namespace xo {
|
|||
return free_ptr_ - checkpoint_;
|
||||
}
|
||||
|
||||
bool
|
||||
ArenaAlloc::debug_flag() const
|
||||
{
|
||||
return debug_flag_;
|
||||
}
|
||||
|
||||
void
|
||||
ArenaAlloc::clear()
|
||||
{
|
||||
this->set_free_ptr(lo_);
|
||||
#ifdef REDLINE_MEMORY
|
||||
this->limit_ = hi_ - redline_z_;
|
||||
#else
|
||||
this->limit_ = hi_;
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -204,7 +199,7 @@ namespace xo {
|
|||
|
||||
std::byte * retval = this->free_ptr_;
|
||||
|
||||
log && log(xtag("self", name_), xtag("z0", z0), xtag("+pad", dz), xtag("z1", z1));
|
||||
log && log(xtag("self", name_), xtag("z0", z0), xtag("+pad", dz), xtag("z1", z1), xtag("avail", this->available()));
|
||||
|
||||
if (free_ptr_ + z1 > limit_) {
|
||||
return nullptr;
|
||||
|
|
@ -215,13 +210,6 @@ namespace xo {
|
|||
return retval;
|
||||
}
|
||||
|
||||
#ifdef REDLINE_MEMORY
|
||||
void
|
||||
ArenaAlloc::release_redline_memory() {
|
||||
this->limit_ = this->hi_;
|
||||
}
|
||||
#endif
|
||||
|
||||
} /*namespace gc*/
|
||||
} /*namespace xo*/
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ set(SELF_SRCS
|
|||
ObjectStatistics.cpp
|
||||
Object.cpp
|
||||
Forwarding1.cpp
|
||||
generation.cpp
|
||||
)
|
||||
|
||||
xo_add_shared_library4(${SELF_LIB} ${PROJECT_NAME}Targets ${PROJECT_VERSION} 1 ${SELF_SRCS})
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ namespace xo {
|
|||
void
|
||||
Forwarding1::display(std::ostream & os) const
|
||||
{
|
||||
os << "<fwd" << xtag("dest", (void*)dest_.ptr()) << ">";
|
||||
os << "<fwd" << xtag("dest-td", dest_->self_tp().td()->short_name()) << ">";
|
||||
}
|
||||
|
||||
Object *
|
||||
|
|
|
|||
137
src/alloc/GC.cpp
137
src/alloc/GC.cpp
|
|
@ -175,7 +175,8 @@ namespace xo {
|
|||
GcStatisticsExt retval = GcStatisticsExt(this->native_gc_statistics());
|
||||
|
||||
retval.nursery_z_ = nursery_[role2int(role::to_space)]->size();
|
||||
retval.nursery_before_checkpoint_z_ = nursery_[role2int(role::to_space)]->before_checkpoint();
|
||||
retval.nursery_before_checkpoint_z_ = this->nursery_to()->before_checkpoint();
|
||||
retval.nursery_after_checkpoint_z_ = this->nursery_to()->after_checkpoint();
|
||||
retval.tenured_z_ = tenured_[role2int(role::to_space)]->size();
|
||||
|
||||
return retval;
|
||||
|
|
@ -254,21 +255,9 @@ namespace xo {
|
|||
{
|
||||
std::byte * x = nursery_[role2int(role::to_space)]->alloc(z);
|
||||
|
||||
if (!x) {
|
||||
/* ListAlloc won't fail -- instead will increase heap size */
|
||||
/* ListAlloc won't fail unless we exhaust memory -- instead will increase heap size */
|
||||
|
||||
this->request_gc(generation::nursery);
|
||||
|
||||
#ifdef REDLINE_MEMORY
|
||||
if (incr_gc_pending_ || full_gc_pending_)
|
||||
nursery_[role2int(role::to_space)]->release_redline_memory();
|
||||
|
||||
/* try (just once) more, maybe request fits in redline space */
|
||||
x = nursery_[role2int(role::to_space)]->alloc(z);
|
||||
#endif
|
||||
|
||||
assert(x);
|
||||
}
|
||||
assert(x);
|
||||
|
||||
return x;
|
||||
}
|
||||
|
|
@ -308,18 +297,6 @@ namespace xo {
|
|||
log && log("nursery");
|
||||
|
||||
retval = nursery_[role2int(role::to_space)]->alloc(z);
|
||||
|
||||
if (!retval) {
|
||||
/* nursery space exhausted !? */
|
||||
|
||||
this->request_gc(generation::nursery);
|
||||
|
||||
#ifdef REDLINE_MEMORY
|
||||
nursery_[role2int(role::to_space)]->release_redline_memory();
|
||||
#endif
|
||||
|
||||
retval = nursery_[role2int(role::to_space)]->alloc(z);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
|
@ -393,14 +370,6 @@ namespace xo {
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef REDLINE_MEMORY
|
||||
void
|
||||
GC::release_redline_memory()
|
||||
{
|
||||
// not supported feature for GC
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
GC::swap_nursery()
|
||||
{
|
||||
|
|
@ -428,40 +397,62 @@ namespace xo {
|
|||
void
|
||||
GC::swap_spaces(generation target)
|
||||
{
|
||||
scope log(XO_DEBUG(this->debug_flag()));
|
||||
scope log(XO_DEBUG(this->debug_flag()), xtag("upto", target));
|
||||
|
||||
// will be copying into the memory regions currently labelled FromSpace
|
||||
|
||||
/* gc will copy some to-be-determined amount in [0..promote_z]
|
||||
from nursery->tenured generation.
|
||||
*/
|
||||
std::size_t promote_z = nursery_[role2int(role::to_space)]->before_checkpoint();
|
||||
std::size_t max_promote_z = nursery_[role2int(role::to_space)]->before_checkpoint();
|
||||
|
||||
log && log(xtag("max_promote_z", max_promote_z));
|
||||
|
||||
if (target == generation::tenured) {
|
||||
/* gc on tenured generation may need this much space */
|
||||
std::size_t tenured_z = (tenured_[role2int(role::to_space)]->allocated()
|
||||
+ promote_z
|
||||
+ full_gc_threshold_);
|
||||
std::size_t need_tenured_z = (tenured_[role2int(role::to_space)]->allocated()
|
||||
+ max_promote_z
|
||||
+ full_gc_threshold_);
|
||||
|
||||
tenured_[role2int(role::from_space)]->reset(tenured_z);
|
||||
log && log("need_tenured_z", need_tenured_z);
|
||||
|
||||
tenured_from()->reset(need_tenured_z);
|
||||
|
||||
this->swap_tenured();
|
||||
} else {
|
||||
if (tenured_[role2int(role::to_space)]->available() < promote_z) {
|
||||
tenured_[role2int(role::to_space)]->expand(promote_z);
|
||||
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) {
|
||||
ListAlloc * tenured_to = this->tenured_to();
|
||||
|
||||
tenured_to->expand(max_promote_z, tenured_to->name() + "+");
|
||||
}
|
||||
}
|
||||
|
||||
nursery_[role2int(role::from_space)]->reset(nursery_[role2int(role::to_space)]->allocated()
|
||||
- promote_z
|
||||
+ incr_gc_threshold_);
|
||||
/* subtracting max_promote_z is correct here, since anything not promoted is garbage */
|
||||
std::size_t need_nursery_z = (nursery(role::to_space)->allocated()
|
||||
- max_promote_z
|
||||
+ incr_gc_threshold_);
|
||||
|
||||
log && log(xtag("need_nursery_z", need_nursery_z));
|
||||
|
||||
/* (from-space is about to become to-space, to receive surviving nursery objects) */
|
||||
nursery(role::from_space)->reset(need_nursery_z);
|
||||
|
||||
this->swap_nursery();
|
||||
|
||||
this->swap_mutation_log();
|
||||
|
||||
log && log(xtag("nursery.from", nursery_[role2int(role::from_space)]->name()));
|
||||
log && log(xtag("nursery.to", nursery_[role2int(role::to_space) ]->name()));
|
||||
log && log(xtag("tenured.from", tenured_[role2int(role::from_space)]->name()));
|
||||
log && log(xtag("tenured.to", tenured_[role2int(role::to_space) ]->name()));
|
||||
ListAlloc * N_from = nursery(role::from_space);
|
||||
log && log(xtag("nursery.from", N_from->name()), xtag("size", N_from->size()));
|
||||
ListAlloc * N_to = nursery(role::to_space);
|
||||
log && log(xtag("nursery.to", N_to->name()), xtag("size", N_to->size()));
|
||||
ListAlloc * T_from = tenured(role::from_space);
|
||||
log && log(xtag("tenured.from", T_from->name()), xtag("size", T_from->size()));
|
||||
ListAlloc * T_to = tenured(role::to_space);
|
||||
log && log(xtag("tenured.to", T_to->name()), xtag("size", T_to->size()));
|
||||
|
||||
} /*swap_spaces*/
|
||||
|
||||
|
|
@ -504,8 +495,12 @@ namespace xo {
|
|||
void
|
||||
GC::copy_globals(generation upto)
|
||||
{
|
||||
scope log(XO_DEBUG(config_.debug_flag_),
|
||||
xtag("roots", gc_root_v_.size()));
|
||||
|
||||
for (Object ** pp_root : gc_root_v_) {
|
||||
this->copy_object(pp_root, upto, &object_statistics_sae_[gen2int(upto)]);
|
||||
this->copy_object(pp_root, upto,
|
||||
&object_statistics_sae_[gen2int(upto)]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -750,28 +745,30 @@ namespace xo {
|
|||
{
|
||||
scope log(XO_DEBUG(config_.debug_flag_));
|
||||
|
||||
std::size_t N_allocated = nursery_[role2int(role::from_space)]->after_checkpoint();
|
||||
std::size_t T_allocated = tenured_[role2int(role::from_space)]->after_checkpoint();
|
||||
std::size_t N_allocated = nursery_from()->after_checkpoint();
|
||||
std::size_t T_allocated = tenured_from()->after_checkpoint();
|
||||
|
||||
std::size_t N_before_gc = nursery_[role2int(role::from_space)]->allocated();
|
||||
std::size_t T_before_gc = tenured_[role2int(role::from_space)]->allocated();
|
||||
std::size_t N_before_gc = nursery_from()->allocated();
|
||||
std::size_t T_before_gc = tenured_from()->allocated();
|
||||
|
||||
std::size_t N_after_gc = nursery_[role2int(role::to_space)]->allocated();
|
||||
std::size_t T_after_gc = tenured_[role2int(role::to_space)]->allocated();
|
||||
std::size_t N_after_gc = nursery_to()->allocated();
|
||||
std::size_t T_after_gc = tenured_to()->allocated();
|
||||
//std::byte * N_free_ptr = nursery_[role2int(role::to_space)]->free_ptr();
|
||||
|
||||
std::size_t promote_z = gc_statistics_.total_promoted_ - gc_statistics_.total_promoted_sab_;
|
||||
std::size_t promote_z = (gc_statistics_.total_promoted_
|
||||
- gc_statistics_.total_promoted_sab_);
|
||||
|
||||
this->nursery_[role2int(role::from_space)]->reset(0);
|
||||
this->tenured_[role2int(role::from_space)]->reset(0);
|
||||
/* Don't reset from-space here, it's unnecessary.
|
||||
* Would be permissible, but interferes with GC object modelling in
|
||||
* xo-object/utest/GC.test.cpp
|
||||
*/
|
||||
//this->nursery_[role2int(role::from_space)]->reset(0);
|
||||
//this->tenured_[role2int(role::from_space)]->reset(0);
|
||||
|
||||
/* objects currenty in to-space nursery have survived one collection */
|
||||
this->nursery_[role2int(role::to_space)]->checkpoint();
|
||||
|
||||
// nursery_[role2int(role::to_space)]->set_redline(nursery_[role2int(role::to_space)]->allocated() + incr_gc_threshold_)
|
||||
|
||||
this->nursery_to()->checkpoint();
|
||||
if (upto == generation::tenured)
|
||||
this->tenured_[role2int(role::to_space)]->checkpoint();
|
||||
this->tenured_to()->checkpoint();
|
||||
|
||||
if (log) {
|
||||
log(xtag("N_allocated", N_allocated));
|
||||
|
|
@ -819,7 +816,7 @@ namespace xo {
|
|||
|
||||
this->capture_object_statistics(upto, capture_phase::sab);
|
||||
|
||||
log && log("step 1 : swap to/from roles");
|
||||
log && log("step 1 : swap to/from roles");
|
||||
|
||||
this->swap_spaces(upto);
|
||||
|
||||
|
|
@ -829,15 +826,15 @@ namespace xo {
|
|||
|
||||
log && log("step 2b: TODO: copy pinned");
|
||||
|
||||
log && log("step 3 : forward mutation log");
|
||||
log && log("step 3 : forward mutation log");
|
||||
|
||||
this->forward_mutation_log(upto);
|
||||
|
||||
log && log("step 4 : TODO: notify destructor log");
|
||||
log && log("step 4 : TODO: notify destructor log");
|
||||
|
||||
log && log("step 5 : TODO: keep reachable weak pointers");
|
||||
log && log("step 5 : TODO: keep reachable weak pointers");
|
||||
|
||||
log && log("step 6 : cleanup");
|
||||
log && log("step 6 : cleanup");
|
||||
|
||||
this->cleanup_phase(upto);
|
||||
|
||||
|
|
|
|||
|
|
@ -32,12 +32,12 @@ namespace xo {
|
|||
PerGenerationStatistics::display(std::ostream & os) const
|
||||
{
|
||||
os << "<PerGenerationStatistics"
|
||||
<< rtag("used", used_z_)
|
||||
<< rtag("n_gc", n_gc_)
|
||||
<< rtag("new_alloc_z", new_alloc_z_)
|
||||
<< rtag("scanned_z", scanned_z_)
|
||||
<< rtag("survive_z", survive_z_)
|
||||
<< rtag("promote_z", promote_z_)
|
||||
<< xrtag("used", used_z_)
|
||||
<< xrtag("n_gc", n_gc_)
|
||||
<< xrtag("new_alloc_z", new_alloc_z_)
|
||||
<< xrtag("scanned_z", scanned_z_)
|
||||
<< xrtag("survive_z", survive_z_)
|
||||
<< xrtag("promote_z", promote_z_)
|
||||
<< ">";
|
||||
}
|
||||
|
||||
|
|
@ -62,9 +62,9 @@ namespace xo {
|
|||
GcStatistics::display(std::ostream & os) const
|
||||
{
|
||||
os << "<GcStatistics"
|
||||
<< rtag("gen_v", gen_v_)
|
||||
<< rtag("total_allocated", total_allocated_)
|
||||
<< rtag("total_promoted_sab", total_promoted_sab_)
|
||||
<< xrtag("gen_v", gen_v_)
|
||||
<< xrtag("total_allocated", total_allocated_)
|
||||
<< xrtag("total_promoted_sab", total_promoted_sab_)
|
||||
// total_promoted
|
||||
// n_mtuation
|
||||
// n_logged_mutation
|
||||
|
|
@ -78,17 +78,17 @@ namespace xo {
|
|||
GcStatisticsExt::display(std::ostream & os) const
|
||||
{
|
||||
os << "<GcStatisticsExt"
|
||||
<< rtag("gen_v", gen_v_)
|
||||
<< rtag("total_allocated", total_allocated_)
|
||||
<< rtag("total_promoted_sab", total_promoted_)
|
||||
<< rtag("nursery_z", nursery_z_)
|
||||
<< rtag("nursery_before_ckp_z", nursery_before_checkpoint_z_)
|
||||
<< rtag("nursery_after_ckp_z", nursery_after_checkpoint_z_)
|
||||
<< rtag("tenured_z", tenured_z_)
|
||||
<< rtag("n_mutation", n_mutation_)
|
||||
<< rtag("n_logged_mutation", n_logged_mutation_)
|
||||
<< rtag("n_xgen_mutation", n_xgen_mutation_)
|
||||
<< rtag("n_xkcp_mutation", n_xckp_mutation_)
|
||||
<< xrtag("gen_v", gen_v_)
|
||||
<< xrtag("total_allocated", total_allocated_)
|
||||
<< xrtag("total_promoted_sab", total_promoted_)
|
||||
<< xrtag("nursery_z", nursery_z_)
|
||||
<< xrtag("nursery_before_ckp_z", nursery_before_checkpoint_z_)
|
||||
<< xrtag("nursery_after_ckp_z", nursery_after_checkpoint_z_)
|
||||
<< xrtag("tenured_z", tenured_z_)
|
||||
<< xrtag("n_mutation", n_mutation_)
|
||||
<< xrtag("n_logged_mutation", n_logged_mutation_)
|
||||
<< xrtag("n_xgen_mutation", n_xgen_mutation_)
|
||||
<< xrtag("n_xckp_mutation", n_xckp_mutation_)
|
||||
// << xtag("per_type_stats", per_type_stats_)
|
||||
<< ">";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
#include "ListAlloc.hpp"
|
||||
#include "ArenaAlloc.hpp"
|
||||
#include "xo/indentlog/scope.hpp"
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
|
||||
|
|
@ -13,9 +14,6 @@ namespace xo {
|
|||
ListAlloc::ListAlloc(std::unique_ptr<ArenaAlloc> hd,
|
||||
ArenaAlloc * marked,
|
||||
std::size_t cz, std::size_t nz, std::size_t tz,
|
||||
#ifdef REDLINE_MEMORY
|
||||
bool use_redline,
|
||||
#endif
|
||||
bool debug_flag)
|
||||
: start_z_{cz},
|
||||
hd_{std::move(hd)},
|
||||
|
|
@ -24,9 +22,6 @@ namespace xo {
|
|||
current_z_{cz},
|
||||
next_z_{nz},
|
||||
total_z_{tz},
|
||||
#ifdef REDLINE_MEMOORY
|
||||
use_redline_{use_redline},
|
||||
#endif
|
||||
debug_flag_{debug_flag}
|
||||
{}
|
||||
|
||||
|
|
@ -39,9 +34,6 @@ namespace xo {
|
|||
ListAlloc::make(const std::string & name, std::size_t cz, std::size_t nz, bool debug_flag)
|
||||
{
|
||||
std::unique_ptr<ArenaAlloc> hd{ArenaAlloc::make(name,
|
||||
#ifdef REDLINE_MEMORY
|
||||
0,
|
||||
#endif
|
||||
cz, debug_flag)};
|
||||
|
||||
if (!hd)
|
||||
|
|
@ -52,9 +44,6 @@ namespace xo {
|
|||
up<ListAlloc> retval{new ListAlloc(std::move(hd),
|
||||
marked,
|
||||
cz, nz, cz,
|
||||
#ifdef REDLINE_MEMORY
|
||||
false /*!use_redline*/,
|
||||
#endif
|
||||
debug_flag)};
|
||||
|
||||
return retval;
|
||||
|
|
@ -134,34 +123,31 @@ namespace xo {
|
|||
bool
|
||||
ListAlloc::is_before_checkpoint(const void * x) const {
|
||||
if (!marked_)
|
||||
return false;
|
||||
return true;
|
||||
|
||||
if ((marked_ == hd_.get()) && hd_->contains(x))
|
||||
return hd_->is_before_checkpoint(x);
|
||||
if (marked_ && marked_->contains(x))
|
||||
return marked_->is_before_checkpoint(x);
|
||||
|
||||
/*
|
||||
* 1. allocs in full_l_ appear in youngest-to-oldest order
|
||||
* 2. allocators that appear before marked_ in full_l_ count as 'after checkpoint'
|
||||
* 3. allocators that appear after marked_ in full_l_ count as 'before checkpoint'
|
||||
* 1. allocs in full_l_ appear in oldest-to-youngest order
|
||||
* 2. allocators that appear before marked_ in full_l_ count as 'before checkpoint'
|
||||
* 3. allocators that appear after marked_ in full_l_ count as 'after checkpoint'
|
||||
*/
|
||||
|
||||
bool younger_than_marked = true;
|
||||
bool older_than_marked = true;
|
||||
|
||||
for (const auto & alloc : full_l_) {
|
||||
if (younger_than_marked) {
|
||||
if (older_than_marked) {
|
||||
if (alloc.get() == marked_) {
|
||||
/* nothing else to test on this iteration,
|
||||
* already checked .marked_ specifically
|
||||
*/
|
||||
younger_than_marked = false;
|
||||
break;
|
||||
} else {
|
||||
/* after checkpoint */
|
||||
/* before checkpoint */
|
||||
if (alloc->contains(x))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if (alloc->contains(x))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -171,55 +157,63 @@ namespace xo {
|
|||
std::size_t
|
||||
ListAlloc::before_checkpoint() const
|
||||
{
|
||||
scope log(XO_DEBUG(false && debug_flag_), xtag("marked", marked_ ? marked_->name() : ""));
|
||||
|
||||
if (marked_) {
|
||||
if (full_l_.empty()) {
|
||||
assert(marked_ == hd_.get());
|
||||
|
||||
return marked_->before_checkpoint();
|
||||
} else {
|
||||
std::size_t z = 0;
|
||||
|
||||
/* control here: .marked & .full_l non-empty. */
|
||||
if (hd_.get() == marked_) {
|
||||
z += hd_->before_checkpoint();
|
||||
|
||||
/* anything in .full_l is older than marked .hd */
|
||||
for (const auto & alloc : full_l_) {
|
||||
z += alloc->allocated();
|
||||
}
|
||||
|
||||
return z;
|
||||
} else {
|
||||
/* messiest case: .marked is true,
|
||||
* and not the youngest arena
|
||||
*/
|
||||
|
||||
/* full_l always in increasing time order: oldest-to-youngest order */
|
||||
size_t i_alloc = 0;
|
||||
for (const auto & alloc : full_l_) {
|
||||
log && log(xtag("i_alloc", i_alloc),
|
||||
xtag("alloc", alloc->name()),
|
||||
xtag("z", z));
|
||||
|
||||
if (alloc.get() == marked_) {
|
||||
log && log("marked", xtag("+z", marked_->before_checkpoint()));
|
||||
z += marked_->before_checkpoint();
|
||||
break;
|
||||
} else {
|
||||
log && log("older than marked", xtag("+z", alloc->allocated()));
|
||||
z += alloc->allocated();
|
||||
}
|
||||
++i_alloc;
|
||||
}
|
||||
}
|
||||
|
||||
return z;
|
||||
}
|
||||
} else {
|
||||
/* count everything allocated */
|
||||
/* count *everything* allocated */
|
||||
return this->allocated();
|
||||
}
|
||||
|
||||
std::size_t z = 0;
|
||||
|
||||
/* control here: .marked & .full_l non-empty. */
|
||||
if (hd_.get() == marked_) {
|
||||
z += hd_->before_checkpoint();
|
||||
|
||||
/* anything in .full_l older than marked .hd */
|
||||
for (const auto & alloc : full_l_) {
|
||||
z += alloc->allocated();
|
||||
}
|
||||
|
||||
return z;
|
||||
} else {
|
||||
/* messiest case: .marked is true,
|
||||
* and not the youngest arena
|
||||
*/
|
||||
bool younger_than_marked = true;
|
||||
|
||||
for (const auto & alloc : full_l_) {
|
||||
if (younger_than_marked) {
|
||||
if (alloc.get() == marked_) {
|
||||
younger_than_marked = false;
|
||||
z += marked_->before_checkpoint();
|
||||
} else {
|
||||
;
|
||||
}
|
||||
} else {
|
||||
z += alloc->allocated();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return z;
|
||||
}
|
||||
|
||||
std::size_t
|
||||
ListAlloc::after_checkpoint() const
|
||||
{
|
||||
scope log(XO_DEBUG(false && debug_flag_), xtag("marked", marked_ ? marked_->name() : ""));
|
||||
|
||||
if (!marked_)
|
||||
return 0;
|
||||
|
||||
|
|
@ -229,25 +223,44 @@ namespace xo {
|
|||
return marked_->after_checkpoint();
|
||||
}
|
||||
|
||||
bool younger_than_marked = true;
|
||||
bool older_than_marked = true;
|
||||
|
||||
std::size_t z = 0;
|
||||
|
||||
std::size_t i_alloc = 0;
|
||||
for (const auto & alloc : full_l_) {
|
||||
if (younger_than_marked) {
|
||||
log && log(xtag("i_alloc", i_alloc),
|
||||
xtag("alloc", alloc->name()),
|
||||
xtag("z", z));
|
||||
|
||||
if (older_than_marked) {
|
||||
if (alloc.get() == marked_) {
|
||||
younger_than_marked = false;
|
||||
log && log("marked", xtag("+z", marked_->after_checkpoint()));
|
||||
older_than_marked = false;
|
||||
z += marked_->after_checkpoint();
|
||||
break;
|
||||
} else {
|
||||
z += alloc->allocated();
|
||||
}
|
||||
} else {
|
||||
/* younger than marked */
|
||||
log && log("younger", xtag("+z", alloc->allocated()));
|
||||
z += alloc->allocated();
|
||||
}
|
||||
|
||||
++i_alloc;
|
||||
}
|
||||
|
||||
/** head must be included, since it's always the youngest bucket **/
|
||||
z += hd_->after_checkpoint();
|
||||
|
||||
log && log("z", z);
|
||||
|
||||
return z;
|
||||
}
|
||||
|
||||
bool
|
||||
ListAlloc::debug_flag() const {
|
||||
return debug_flag_;
|
||||
}
|
||||
|
||||
void
|
||||
ListAlloc::clear() {
|
||||
// general hygiene
|
||||
|
|
@ -258,26 +271,17 @@ namespace xo {
|
|||
current_z_ = 0;
|
||||
next_z_ = 0;
|
||||
total_z_ = 0;
|
||||
#ifdef REDLINE_MEMORY
|
||||
use_redline_ = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool
|
||||
ListAlloc::reset(std::size_t z)
|
||||
{
|
||||
#ifdef REDLINE_MEMORY
|
||||
// warning: hd_->size() does not include redline memory
|
||||
hd_->release_redline_memory();
|
||||
#endif
|
||||
scope log(XO_DEBUG(debug_flag_), xtag("z", z));
|
||||
|
||||
bool recycle_head_bucket = hd_ && (z <= hd_->size());
|
||||
|
||||
this->full_l_.clear();
|
||||
this->marked_ = nullptr;
|
||||
#ifdef REDLINE_MEMORY
|
||||
this->redlined_flag_ = false;
|
||||
#endif
|
||||
|
||||
if (recycle_head_bucket) {
|
||||
this->hd_->clear();
|
||||
|
|
@ -285,16 +289,22 @@ namespace xo {
|
|||
|
||||
return true;
|
||||
} else {
|
||||
std::string old_name = this->hd_->name();
|
||||
|
||||
this->hd_.reset(nullptr);
|
||||
this->total_z_ = 0;
|
||||
|
||||
return this->expand(z);
|
||||
return this->expand(z, old_name + "+");
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
ListAlloc::expand(std::size_t z)
|
||||
ListAlloc::expand(std::size_t z, const std::string & name)
|
||||
{
|
||||
scope log(XO_DEBUG(debug_flag_), xtag("name", name));
|
||||
|
||||
//log && log("before", xtag("before_ckp", this->before_checkpoint()));
|
||||
|
||||
std::size_t cz = current_z_;
|
||||
std::size_t nz = next_z_;
|
||||
std::size_t tz;
|
||||
|
|
@ -305,12 +315,9 @@ namespace xo {
|
|||
nz = tz;
|
||||
} while (cz < z);
|
||||
|
||||
std::string name = hd_->name() + "+exp";
|
||||
log && log("expand to", xtag("cz", cz));
|
||||
|
||||
std::unique_ptr<ArenaAlloc> new_alloc = ArenaAlloc::make(name,
|
||||
#ifdef REDLINE_MEMORY
|
||||
0,
|
||||
#endif
|
||||
cz, debug_flag_);
|
||||
|
||||
if (!new_alloc)
|
||||
|
|
@ -320,41 +327,43 @@ namespace xo {
|
|||
this->next_z_ = nz;
|
||||
this->total_z_ += cz;
|
||||
|
||||
if (hd_)
|
||||
this->full_l_.push_back(std::move(hd_));
|
||||
|
||||
this->hd_ = std::move(new_alloc);
|
||||
|
||||
//log && log("after", xtag("before_ckp", this->before_checkpoint()));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
ListAlloc::checkpoint() {
|
||||
scope log(XO_DEBUG(debug_flag_));
|
||||
|
||||
hd_->checkpoint();
|
||||
|
||||
this->marked_ = hd_.get();
|
||||
|
||||
log && log(xtag("hd", (void*)hd_.get()), xtag("marked", (void*)marked_));
|
||||
}
|
||||
|
||||
std::byte *
|
||||
ListAlloc::alloc(std::size_t z) {
|
||||
scope log(XO_DEBUG(debug_flag_));
|
||||
|
||||
std::byte * retval = hd_->alloc(z);
|
||||
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
if (this->expand(z))
|
||||
log && log("space exhausted -> expand");
|
||||
|
||||
if (this->expand(z, hd_->name() + "+"))
|
||||
return hd_->alloc(z);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#ifdef REDLINE_MEMORY
|
||||
void
|
||||
ListAlloc::release_redline_memory()
|
||||
{
|
||||
if (use_redline_)
|
||||
redlined_flag_ = true;
|
||||
|
||||
this->hd_->release_redline_memory();
|
||||
}
|
||||
#endif
|
||||
} /*namespace gc*/
|
||||
} /*namespace xo*/
|
||||
|
||||
|
|
|
|||
|
|
@ -14,13 +14,13 @@ namespace xo {
|
|||
{
|
||||
os << "<PerObjectTypeStatistics";
|
||||
if (td_)
|
||||
os << rtag("td", td_->short_name());
|
||||
os << xrtag("td", td_->short_name());
|
||||
else
|
||||
os << rtag("td", "nullptr");
|
||||
os << rtag("scanned_n", scanned_n_)
|
||||
<< rtag("scanned_z", scanned_z_)
|
||||
<< rtag("survive_n", survive_n_)
|
||||
<< rtag("survive_z", survive_z_)
|
||||
os << xrtag("td", "nullptr");
|
||||
os << xrtag("scanned_n", scanned_n_)
|
||||
<< xrtag("scanned_z", scanned_z_)
|
||||
<< xrtag("survive_n", survive_n_)
|
||||
<< xrtag("survive_z", survive_z_)
|
||||
<< ">";
|
||||
}
|
||||
|
||||
|
|
|
|||
22
src/alloc/generation.cpp
Normal file
22
src/alloc/generation.cpp
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
/* generation.cpp
|
||||
*
|
||||
* author: Roland Conybeare, Aug 2025
|
||||
*/
|
||||
|
||||
#include "generation.hpp"
|
||||
|
||||
namespace xo {
|
||||
namespace gc {
|
||||
const char * gen2str(generation x) {
|
||||
switch (x) {
|
||||
case generation::nursery: return "nursery";
|
||||
case generation::tenured: return "tenured";
|
||||
case generation::N: break;
|
||||
}
|
||||
return "?generation";
|
||||
}
|
||||
|
||||
} /*namespace gc*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/* generation.cpp */
|
||||
|
|
@ -15,14 +15,8 @@ namespace xo {
|
|||
struct testcase_alloc {
|
||||
testcase_alloc(std::size_t rz, std::size_t z)
|
||||
:
|
||||
#ifdef REDLINE_MEMORY
|
||||
redline_z_{rz},
|
||||
#endif
|
||||
arena_z_{z} {}
|
||||
|
||||
#ifdef REDLINE_MEMORY
|
||||
std::size_t redline_z_;
|
||||
#endif
|
||||
std::size_t arena_z_;
|
||||
|
||||
};
|
||||
|
|
@ -42,9 +36,6 @@ namespace xo {
|
|||
constexpr bool c_debug_flag = false;
|
||||
|
||||
auto alloc = ArenaAlloc::make("linearalloc",
|
||||
#ifdef REDLINE_MEMORY
|
||||
tc.redline_z_,
|
||||
#endif
|
||||
tc.arena_z_, c_debug_flag);
|
||||
|
||||
REQUIRE(alloc.get());
|
||||
|
|
|
|||
|
|
@ -7,7 +7,13 @@ set(UTEST_SRCS
|
|||
alloc_utest_main.cpp
|
||||
IAlloc.test.cpp
|
||||
ArenaAlloc.test.cpp
|
||||
GC.test.cpp)
|
||||
ListAlloc.test.cpp
|
||||
GC.test.cpp
|
||||
GcStatistics.test.cpp
|
||||
ObjectStatistics.test.cpp
|
||||
Forwarding1.test.cpp
|
||||
generation.test.cpp
|
||||
)
|
||||
|
||||
xo_add_utest_executable(${UTEST_EXE} ${UTEST_SRCS})
|
||||
xo_self_dependency(${UTEST_EXE} xo_alloc)
|
||||
|
|
|
|||
85
utest/Forwarding1.test.cpp
Normal file
85
utest/Forwarding1.test.cpp
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
/* Forwarding1.test.cpp
|
||||
*
|
||||
* author: Roland Conybeare, Aug 2025
|
||||
*/
|
||||
|
||||
#include "Forwarding1.hpp"
|
||||
#include "ArenaAlloc.hpp"
|
||||
#include "xo/reflect/Reflect.hpp"
|
||||
#include <catch2/catch.hpp>
|
||||
#include <cstring>
|
||||
|
||||
namespace xo {
|
||||
using xo::reflect::Reflect;
|
||||
using xo::obj::Forwarding1;
|
||||
|
||||
namespace gc {
|
||||
namespace {
|
||||
class DummyObject : public Object {
|
||||
public:
|
||||
explicit DummyObject(const char * data) {
|
||||
::strncpy(data_.data(), data, 128);
|
||||
}
|
||||
|
||||
gp<Object> member() const { return member_; }
|
||||
void assign_member(Object * x) {
|
||||
Object::mm->assign_member(this, member_.ptr_address(), x);
|
||||
}
|
||||
|
||||
TaggedPtr self_tp() const final override {
|
||||
return Reflect::make_tp(const_cast<DummyObject*>(this));
|
||||
}
|
||||
|
||||
void display(std::ostream & os) const final override { os << data_; }
|
||||
|
||||
virtual std::size_t _shallow_size() const final override { return sizeof(*this); }
|
||||
virtual Object * _shallow_copy() const final override { return new (Cpof(this)) DummyObject(*this); }
|
||||
virtual std::size_t _forward_children() final override { return _shallow_size(); }
|
||||
|
||||
private:
|
||||
std::array<char, 128> data_;
|
||||
gp<Object> member_;
|
||||
};
|
||||
}
|
||||
|
||||
TEST_CASE("Forwarding1", "[gc][alloc]")
|
||||
{
|
||||
bool saved = tag_config::tag_color_enabled;
|
||||
tag_config::tag_color_enabled = false;
|
||||
|
||||
gp<Object> obj = new DummyObject("Well, I wasn't expecting that!");
|
||||
gp<Forwarding1> fwd = new Forwarding1(obj);
|
||||
|
||||
REQUIRE(fwd->_destination() == obj.ptr());
|
||||
REQUIRE(fwd->_offset_destination(fwd.ptr()) == obj.ptr());
|
||||
|
||||
REQUIRE(fwd->self_tp().td()->short_name() == "Forwarding1");
|
||||
|
||||
std::stringstream ss;
|
||||
ss << fwd;
|
||||
|
||||
REQUIRE(ss.str() == "<fwd :dest-td DummyObject>");
|
||||
|
||||
tag_config::tag_color_enabled = saved;
|
||||
}
|
||||
|
||||
TEST_CASE("IAlloc.assign_member", "[gc][alloc]")
|
||||
{
|
||||
/* not giving this nit it's own translation unit.
|
||||
*/
|
||||
|
||||
gp<DummyObject> obj = new DummyObject("This also a surprise..");
|
||||
|
||||
up<ArenaAlloc> arena = ArenaAlloc::make("test", 1024, false);
|
||||
|
||||
Object::mm = arena.get();
|
||||
|
||||
obj->assign_member(obj.ptr());
|
||||
|
||||
REQUIRE(obj->member().ptr() == obj.ptr());
|
||||
}
|
||||
|
||||
} /*namespace gc*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/* end Forwarding1.test.cpp */
|
||||
|
|
@ -37,6 +37,7 @@ namespace xo {
|
|||
.initial_tenured_z_ = tc.tenured_z_});
|
||||
|
||||
REQUIRE(gc.get());
|
||||
REQUIRE(gc->name() == "GC");
|
||||
REQUIRE(gc->size() == tc.nursery_z_ + tc.tenured_z_);
|
||||
REQUIRE(gc->allocated() == 0);
|
||||
REQUIRE(gc->available() == tc.nursery_z_);
|
||||
|
|
|
|||
216
utest/GcStatistics.test.cpp
Normal file
216
utest/GcStatistics.test.cpp
Normal file
|
|
@ -0,0 +1,216 @@
|
|||
/* @file GcStatistics.test.cpp
|
||||
*
|
||||
* author: Roland Conybeare, Aug 2025
|
||||
*/
|
||||
|
||||
#include "xo/alloc/GcStatistics.hpp"
|
||||
#include "xo/indentlog/scope.hpp"
|
||||
#include "xo/indentlog/print/tostr.hpp"
|
||||
#include "xo/indentlog/print/hex.hpp"
|
||||
#include <catch2/catch.hpp>
|
||||
#include <ranges>
|
||||
|
||||
namespace xo {
|
||||
using xo::gc::GcStatistics;
|
||||
using xo::gc::GcStatisticsExt;
|
||||
using xo::gc::PerGenerationStatistics;
|
||||
using xo::print::ppconfig;
|
||||
|
||||
namespace ut {
|
||||
TEST_CASE("PerGenerationStatistics", "[alloc][gc]")
|
||||
{
|
||||
bool saved = tag_config::tag_color_enabled;
|
||||
|
||||
tag_config::tag_color_enabled = false;
|
||||
|
||||
PerGenerationStatistics stats;
|
||||
|
||||
std::string s = tostr(stats);
|
||||
|
||||
//std::cerr << hex_view(s.c_str(), s.c_str() + s.length(), true /*as_text*/) << std::endl;
|
||||
|
||||
REQUIRE(s == "<PerGenerationStatistics :used 0 :n_gc 0 :new_alloc_z 0 :scanned_z 0 :survive_z 0 :promote_z 0>");
|
||||
|
||||
tag_config::tag_color_enabled = saved;
|
||||
}
|
||||
|
||||
TEST_CASE("GcStatistics", "[alloc][gc]")
|
||||
{
|
||||
bool saved = tag_config::tag_color_enabled;
|
||||
|
||||
tag_config::tag_color_enabled = false;
|
||||
|
||||
GcStatistics stats;
|
||||
|
||||
std::string s = tostr(stats);
|
||||
|
||||
REQUIRE(s ==
|
||||
"<GcStatistics"
|
||||
/**/" :gen_v ["
|
||||
/***/ "<PerGenerationStatistics"
|
||||
/****/ " :used 0"
|
||||
/****/ " :n_gc 0"
|
||||
/****/ " :new_alloc_z 0"
|
||||
/****/ " :scanned_z 0"
|
||||
/****/ " :survive_z 0"
|
||||
/****/ " :promote_z 0"
|
||||
/***/ ">"
|
||||
/***/ " <PerGenerationStatistics"
|
||||
/****/ " :used 0"
|
||||
/****/ " :n_gc 0"
|
||||
/****/ " :new_alloc_z 0"
|
||||
/****/ " :scanned_z 0"
|
||||
/****/ " :survive_z 0"
|
||||
/****/ " :promote_z 0"
|
||||
/***/ ">]"
|
||||
/**/ " :total_allocated 0"
|
||||
/**/ " :total_promoted_sab 0"
|
||||
">");
|
||||
|
||||
tag_config::tag_color_enabled = saved;
|
||||
}
|
||||
|
||||
TEST_CASE("GcStatisticsExt", "[alloc][gc]")
|
||||
{
|
||||
bool saved = tag_config::tag_color_enabled;
|
||||
|
||||
tag_config::tag_color_enabled = false;
|
||||
|
||||
GcStatisticsExt stats({});
|
||||
|
||||
std::string s = tostr(stats);
|
||||
|
||||
REQUIRE(s == "<GcStatisticsExt :gen_v [<PerGenerationStatistics :used 0 :n_gc 0 :new_alloc_z 0 :scanned_z 0 :survive_z 0 :promote_z 0> <PerGenerationStatistics :used 0 :n_gc 0 :new_alloc_z 0 :scanned_z 0 :survive_z 0 :promote_z 0>] :total_allocated 0 :total_promoted_sab 0 :nursery_z 0 :nursery_before_ckp_z 0 :nursery_after_ckp_z 0 :tenured_z 0 :n_mutation 0 :n_logged_mutation 0 :n_xgen_mutation 0 :n_xckp_mutation 0>");
|
||||
|
||||
tag_config::tag_color_enabled = saved;
|
||||
}
|
||||
|
||||
TEST_CASE("GcStatistics-pretty", "[alloc][gc][pretty]")
|
||||
{
|
||||
bool saved = tag_config::tag_color_enabled;
|
||||
|
||||
tag_config::tag_color_enabled = false;
|
||||
|
||||
std::stringstream ss;
|
||||
ppconfig ppc;
|
||||
GcStatistics stats;
|
||||
|
||||
std::string actual = toppstr2(ppc, stats);
|
||||
std::string expected
|
||||
= ("<GcStatistics\n"
|
||||
" :gen_v\n"
|
||||
" [ <PerGenerationStatistics\n"
|
||||
" :used_z 0\n"
|
||||
" :n_gc 0\n"
|
||||
" :new_alloc_z 0\n"
|
||||
" :scanned_z 0\n"
|
||||
" :survive_z 0\n"
|
||||
" :promote_z 0>,\n"
|
||||
" <PerGenerationStatistics\n"
|
||||
" :used_z 0\n"
|
||||
" :n_gc 0\n"
|
||||
" :new_alloc_z 0\n"
|
||||
" :scanned_z 0\n"
|
||||
" :survive_z 0\n"
|
||||
" :promote_z 0> ]\n"
|
||||
" :total_allocated 0\n"
|
||||
" :total_promoted_sab 0\n"
|
||||
" :total_promoted 0\n"
|
||||
" :n_mutation 0\n"
|
||||
" :n_logged_mutation 0\n"
|
||||
" :n_xgen_mutation 0\n"
|
||||
" :n_xckp_mutation 0>");
|
||||
|
||||
if (actual != expected) {
|
||||
CHECK(actual == expected);
|
||||
CHECK(actual.length() == expected.length());
|
||||
|
||||
auto a_ix = actual.begin();
|
||||
auto e_ix = expected.begin();
|
||||
|
||||
std::size_t pos = 0;
|
||||
while (a_ix != actual.end() && e_ix != expected.end()) {
|
||||
INFO(xtag("pos", pos));
|
||||
INFO(xtag("matching_prefix", std::string(actual.c_str(), pos)));
|
||||
|
||||
REQUIRE(*a_ix == *e_ix);
|
||||
|
||||
++a_ix;
|
||||
++e_ix;
|
||||
++pos;
|
||||
}
|
||||
}
|
||||
|
||||
REQUIRE(actual == expected);
|
||||
|
||||
tag_config::tag_color_enabled = saved;
|
||||
}
|
||||
|
||||
TEST_CASE("GcStatisticsExt-pretty", "[alloc][gc][pretty]")
|
||||
{
|
||||
bool saved = tag_config::tag_color_enabled;
|
||||
|
||||
tag_config::tag_color_enabled = false;
|
||||
|
||||
std::stringstream ss;
|
||||
ppconfig ppc;
|
||||
GcStatisticsExt stats({});
|
||||
|
||||
std::string actual = toppstr2(ppc, stats);
|
||||
std::string expected
|
||||
= ("<GcStatisticsExt\n"
|
||||
" :gen_v\n"
|
||||
" [ <PerGenerationStatistics\n"
|
||||
" :used_z 0\n"
|
||||
" :n_gc 0\n"
|
||||
" :new_alloc_z 0\n"
|
||||
" :scanned_z 0\n"
|
||||
" :survive_z 0\n"
|
||||
" :promote_z 0>,\n"
|
||||
" <PerGenerationStatistics\n"
|
||||
" :used_z 0\n"
|
||||
" :n_gc 0\n"
|
||||
" :new_alloc_z 0\n"
|
||||
" :scanned_z 0\n"
|
||||
" :survive_z 0\n"
|
||||
" :promote_z 0> ]\n"
|
||||
" :total_allocated 0\n"
|
||||
" :total_promoted_sab 0\n"
|
||||
" :total_promoted 0\n"
|
||||
" :n_mutation 0\n"
|
||||
" :n_logged_mutation 0\n"
|
||||
" :n_xgen_mutation 0\n"
|
||||
" :n_xckp_mutation 0\n"
|
||||
" :nursery_z 0\n"
|
||||
" :nursery_before_checkpoint_z 0\n"
|
||||
" :nursery_after_checkpoint_z 0\n"
|
||||
" :tenured_z 0>");
|
||||
|
||||
if (actual != expected) {
|
||||
CHECK(actual == expected);
|
||||
CHECK(actual.length() == expected.length());
|
||||
|
||||
auto a_ix = actual.begin();
|
||||
auto e_ix = expected.begin();
|
||||
|
||||
std::size_t pos = 0;
|
||||
while (a_ix != actual.end() && e_ix != expected.end()) {
|
||||
INFO(xtag("pos", pos));
|
||||
INFO(xtag("matching_prefix", std::string(actual.c_str(), pos)));
|
||||
|
||||
REQUIRE(*a_ix == *e_ix);
|
||||
|
||||
++a_ix;
|
||||
++e_ix;
|
||||
++pos;
|
||||
}
|
||||
}
|
||||
|
||||
REQUIRE(actual == expected);
|
||||
|
||||
tag_config::tag_color_enabled = saved;
|
||||
}
|
||||
}
|
||||
} /*namespace xo*/
|
||||
|
||||
/* GcStatistics.test.cpp */
|
||||
58
utest/ListAlloc.test.cpp
Normal file
58
utest/ListAlloc.test.cpp
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
/* ListAlloc.test.cpp
|
||||
*
|
||||
* author: Roland Conybeare, Aug 2025
|
||||
*/
|
||||
|
||||
#include "xo/alloc/ListAlloc.hpp"
|
||||
#include <catch2/catch.hpp>
|
||||
|
||||
namespace xo {
|
||||
using xo::gc::ListAlloc;
|
||||
|
||||
namespace ut {
|
||||
TEST_CASE("ListAlloc", "[alloc][gc]")
|
||||
{
|
||||
/** teeny weeny allocator **/
|
||||
up<ListAlloc> alloc = ListAlloc::make("test", 16, 32, false);
|
||||
|
||||
REQUIRE(alloc->name() == "test");
|
||||
REQUIRE(alloc->size() == 16);
|
||||
REQUIRE(alloc->before_checkpoint() == 0);
|
||||
REQUIRE(alloc->after_checkpoint() == 0);
|
||||
|
||||
/* will expand */
|
||||
std::byte * mem1 = alloc->alloc(20);
|
||||
|
||||
REQUIRE(mem1);
|
||||
REQUIRE(alloc->size() == 16 + 32);
|
||||
/* round up to multiple of 8 */
|
||||
REQUIRE(alloc->before_checkpoint() == 24);
|
||||
REQUIRE(alloc->after_checkpoint() == 0);
|
||||
|
||||
alloc->checkpoint();
|
||||
|
||||
std::byte * mem2 = alloc->alloc(30);
|
||||
|
||||
REQUIRE(mem2);
|
||||
REQUIRE(alloc->size() == 16 + 32 + 48);
|
||||
REQUIRE(alloc->before_checkpoint() == 24);
|
||||
/* round up to multiple of 8 */
|
||||
REQUIRE(alloc->after_checkpoint() == 32);
|
||||
|
||||
std::byte * mem3 = alloc->alloc(40);
|
||||
|
||||
REQUIRE(mem3);
|
||||
REQUIRE(alloc->size() == 16 + 32 + 48 + 80);
|
||||
REQUIRE(alloc->before_checkpoint() == 24);
|
||||
/* already multiple of 8 */
|
||||
REQUIRE(alloc->after_checkpoint() == 32 + 40);
|
||||
|
||||
REQUIRE(alloc->is_before_checkpoint(mem1) == true);
|
||||
REQUIRE(alloc->is_before_checkpoint(mem2) == false);
|
||||
REQUIRE(alloc->is_before_checkpoint(mem3) == false);
|
||||
}
|
||||
|
||||
} /*namespace ut*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/* ListAlloc.test.cpp */
|
||||
185
utest/ObjectStatistics.test.cpp
Normal file
185
utest/ObjectStatistics.test.cpp
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
/* @file ObjectStatistics.test.cpp
|
||||
*
|
||||
* author: Roland Conybeare, Aug 2025
|
||||
*/
|
||||
|
||||
#include "xo/alloc/ObjectStatistics.hpp"
|
||||
#include "xo/reflect/Reflect.hpp"
|
||||
#include "xo/indentlog/scope.hpp"
|
||||
#include "xo/indentlog/print/ppstr.hpp"
|
||||
#include "xo/indentlog/print/tostr.hpp"
|
||||
#include "xo/indentlog/print/hex.hpp"
|
||||
#include <catch2/catch.hpp>
|
||||
|
||||
namespace xo {
|
||||
using xo::gc::ObjectStatistics;
|
||||
using xo::gc::PerObjectTypeStatistics;
|
||||
using xo::reflect::Reflect;
|
||||
using xo::print::ppconfig;
|
||||
|
||||
namespace ut {
|
||||
TEST_CASE("PerObjectTypeStatistics", "[alloc][gc]")
|
||||
{
|
||||
bool saved = tag_config::tag_color_enabled;
|
||||
|
||||
tag_config::tag_color_enabled = false;
|
||||
|
||||
PerObjectTypeStatistics stats;
|
||||
|
||||
std::string s = tostr(stats);
|
||||
|
||||
//std::cerr << hex_view(s.c_str(), s.c_str() + s.length(), true /*as_text*/) << std::endl;
|
||||
|
||||
REQUIRE(s == "<PerObjectTypeStatistics :td nullptr :scanned_n 0 :scanned_z 0 :survive_n 0 :survive_z 0>");
|
||||
|
||||
tag_config::tag_color_enabled = saved;
|
||||
}
|
||||
|
||||
TEST_CASE("PerObjectTypeStatistics-1", "[alloc][gc]")
|
||||
{
|
||||
bool saved = tag_config::tag_color_enabled;
|
||||
|
||||
tag_config::tag_color_enabled = false;
|
||||
|
||||
PerObjectTypeStatistics stats;
|
||||
stats.td_ = Reflect::require<bool>();
|
||||
stats.scanned_n_ = 4;
|
||||
stats.scanned_z_ = 16;
|
||||
stats.survive_n_ = 2;
|
||||
stats.survive_z_ = 8;
|
||||
|
||||
std::string s = tostr(stats);
|
||||
|
||||
//std::cerr << hex_view(s.c_str(), s.c_str() + s.length(), true /*as_text*/) << std::endl;
|
||||
|
||||
REQUIRE(s == "<PerObjectTypeStatistics :td bool :scanned_n 4 :scanned_z 16 :survive_n 2 :survive_z 8>");
|
||||
|
||||
tag_config::tag_color_enabled = saved;
|
||||
}
|
||||
|
||||
TEST_CASE("ObjectStatistics", "[alloc][gc]")
|
||||
{
|
||||
bool saved = tag_config::tag_color_enabled;
|
||||
|
||||
tag_config::tag_color_enabled = false;
|
||||
|
||||
ObjectStatistics stats;
|
||||
|
||||
std::string s = tostr(stats);
|
||||
|
||||
REQUIRE(s == "<ObjectStatistics>");
|
||||
|
||||
tag_config::tag_color_enabled = saved;
|
||||
}
|
||||
|
||||
TEST_CASE("ObjectStatistics-1", "[alloc][gc]")
|
||||
{
|
||||
bool saved = tag_config::tag_color_enabled;
|
||||
|
||||
tag_config::tag_color_enabled = false;
|
||||
|
||||
ObjectStatistics stats;
|
||||
stats.per_type_stats_v_.push_back(PerObjectTypeStatistics());
|
||||
|
||||
std::string s = tostr(stats);
|
||||
|
||||
REQUIRE(s == "<ObjectStatistics :[0] <PerObjectTypeStatistics :td nullptr :scanned_n 0 :scanned_z 0 :survive_n 0 :survive_z 0>>");
|
||||
|
||||
tag_config::tag_color_enabled = saved;
|
||||
}
|
||||
|
||||
TEST_CASE("ObjectStatistics-pretty", "[alloc][gc][pretty]")
|
||||
{
|
||||
bool saved = tag_config::tag_color_enabled;
|
||||
|
||||
tag_config::tag_color_enabled = false;
|
||||
|
||||
std::stringstream ss;
|
||||
ppconfig ppc;
|
||||
ObjectStatistics stats;
|
||||
|
||||
std::string actual = toppstr2(ppc, stats);
|
||||
std::string expected
|
||||
= ("<ObjectTypeStatistics :per_type_stats_v []>");
|
||||
|
||||
if (actual != expected) {
|
||||
CHECK(actual == expected);
|
||||
CHECK(actual.length() == expected.length());
|
||||
|
||||
auto a_ix = actual.begin();
|
||||
auto e_ix = expected.begin();
|
||||
|
||||
std::size_t pos = 0;
|
||||
while (a_ix != actual.end() && e_ix != expected.end()) {
|
||||
INFO(xtag("pos", pos));
|
||||
INFO(xtag("matching_prefix", std::string(actual.c_str(), pos)));
|
||||
|
||||
REQUIRE(*a_ix == *e_ix);
|
||||
|
||||
++a_ix;
|
||||
++e_ix;
|
||||
++pos;
|
||||
}
|
||||
}
|
||||
|
||||
REQUIRE(actual == expected);
|
||||
|
||||
tag_config::tag_color_enabled = saved;
|
||||
}
|
||||
|
||||
TEST_CASE("ObjectStatistics-pretty-1", "[alloc][gc][pretty]")
|
||||
{
|
||||
bool saved = tag_config::tag_color_enabled;
|
||||
|
||||
tag_config::tag_color_enabled = false;
|
||||
|
||||
PerObjectTypeStatistics objstats;
|
||||
objstats.td_ = Reflect::require<bool>();
|
||||
objstats.scanned_n_ = 4;
|
||||
objstats.scanned_z_ = 16;
|
||||
objstats.survive_n_ = 2;
|
||||
objstats.survive_z_ = 8;
|
||||
|
||||
std::stringstream ss;
|
||||
ppconfig ppc;
|
||||
ObjectStatistics stats;
|
||||
stats.per_type_stats_v_.push_back(objstats);
|
||||
|
||||
std::string actual = toppstr2(ppc, stats);
|
||||
|
||||
std::string expected
|
||||
= ("<ObjectTypeStatistics\n"
|
||||
" :per_type_stats_v\n"
|
||||
" [ <PerObjectTypeStatistics\n"
|
||||
" :td bool\n"
|
||||
" :scanned_n 4\n"
|
||||
" :scanned_z 16\n"
|
||||
" :survive_n 2\n"
|
||||
" :survive_z 8> ]>");
|
||||
|
||||
if (actual != expected) {
|
||||
CHECK(actual == expected);
|
||||
CHECK(actual.length() == expected.length());
|
||||
|
||||
auto a_ix = actual.begin();
|
||||
auto e_ix = expected.begin();
|
||||
|
||||
std::size_t pos = 0;
|
||||
while (a_ix != actual.end() && e_ix != expected.end()) {
|
||||
INFO(xtag("pos", pos));
|
||||
INFO(xtag("matching_prefix", std::string(actual.c_str(), pos)));
|
||||
|
||||
REQUIRE(*a_ix == *e_ix);
|
||||
|
||||
++a_ix;
|
||||
++e_ix;
|
||||
++pos;
|
||||
}
|
||||
}
|
||||
|
||||
tag_config::tag_color_enabled = saved;
|
||||
}
|
||||
}
|
||||
} /*namespace xo*/
|
||||
|
||||
/* ObjectStatistics.test.cpp */
|
||||
38
utest/generation.test.cpp
Normal file
38
utest/generation.test.cpp
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
/* generation.test.cpp
|
||||
*
|
||||
* author: Roland Conybeare, Aug 2025
|
||||
*/
|
||||
|
||||
#include "xo/alloc/generation.hpp"
|
||||
#include <catch2/catch.hpp>
|
||||
#include <cstring>
|
||||
|
||||
namespace xo {
|
||||
namespace gc {
|
||||
TEST_CASE("generation", "[gc]") {
|
||||
REQUIRE(::strcmp(gen2str(generation::nursery), "nursery") == 0);
|
||||
REQUIRE(::strcmp(gen2str(generation::tenured), "tenured") == 0);
|
||||
REQUIRE(::strcmp(gen2str(generation::N), "?generation") == 0);
|
||||
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << generation::nursery;
|
||||
REQUIRE(ss.str() == "nursery");
|
||||
}
|
||||
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << generation::tenured;
|
||||
REQUIRE(ss.str() == "tenured");
|
||||
}
|
||||
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << generation::N;
|
||||
REQUIRE(ss.str() == "?generation");
|
||||
}
|
||||
}
|
||||
} /*namespace gc*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/* generation.test.cpp */
|
||||
Loading…
Add table
Add a link
Reference in a new issue