xo-allod: + per-type stats + pretty printing
This commit is contained in:
parent
150bfa4aa2
commit
13f4fb0935
26 changed files with 473 additions and 203 deletions
|
|
@ -6,6 +6,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "IAlloc.hpp"
|
||||
#include "ObjectStatistics.hpp"
|
||||
|
||||
namespace xo {
|
||||
namespace gc {
|
||||
|
|
@ -48,6 +49,9 @@ namespace xo {
|
|||
std::byte * free_ptr() const { return free_ptr_; }
|
||||
void set_free_ptr(std::byte * x);
|
||||
|
||||
void capture_object_statistics(capture_phase phase,
|
||||
ObjectStatistics * p_dest) const;
|
||||
|
||||
// inherited from IAlloc...
|
||||
|
||||
virtual const std::string & name() const final override { return name_; }
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ namespace xo {
|
|||
|
||||
// inherited from Object..
|
||||
virtual TaggedPtr self_tp() const final override;
|
||||
virtual void display(std::ostream & os) const final override;
|
||||
virtual bool _is_forwarded() const final override { return true; }
|
||||
virtual Object * _offset_destination(Object * src) const final override;
|
||||
virtual Object * _destination() final override;
|
||||
|
|
|
|||
|
|
@ -42,6 +42,8 @@ namespace xo {
|
|||
std::size_t initial_tenured_z_ = 0;
|
||||
/** true to permit incremental garbage collection **/
|
||||
bool allow_incremental_gc_ = true;
|
||||
/** true to report statistics **/
|
||||
bool stats_flag_ = false;
|
||||
/** true to enable debug logging **/
|
||||
bool debug_flag_ = false;
|
||||
};
|
||||
|
|
@ -207,6 +209,8 @@ namespace xo {
|
|||
void swap_mutation_log();
|
||||
/** swap roles of FromSpace/ToSpace **/
|
||||
void swap_spaces(generation g);
|
||||
/** scan to-space for object statistics before GC */
|
||||
void capture_object_statistics(generation upto, capture_phase phase);
|
||||
/** copy object **/
|
||||
void copy_object(Object ** addr, generation upto, ObjectStatistics * object_stats);
|
||||
/** copy everything reachable from global gc roots **/
|
||||
|
|
@ -273,6 +277,10 @@ namespace xo {
|
|||
|
||||
/** allocation/collection counters **/
|
||||
GcStatistics gc_statistics_;
|
||||
/** optional per-object-type counters. snapshot at beginning of collection cycle **/
|
||||
std::array<ObjectStatistics, gen2int(generation::N)> object_statistics_sab_;
|
||||
/** optional per-object-type counters. snapshot at end of collection cycle **/
|
||||
std::array<ObjectStatistics, gen2int(generation::N)> object_statistics_sae_;
|
||||
|
||||
/** trigger full GC whenever this much data arrives in tenured generation **/
|
||||
std::size_t full_gc_threshold_ = 0;
|
||||
|
|
|
|||
|
|
@ -6,20 +6,13 @@
|
|||
#pragma once
|
||||
|
||||
#include "generation.hpp"
|
||||
#include "xo/reflect/TypeDescr.hpp"
|
||||
#include "xo/indentlog/print/pretty.hpp"
|
||||
#include <ostream>
|
||||
#include <array>
|
||||
|
||||
namespace xo {
|
||||
namespace gc {
|
||||
/** @class ObjectStatistics
|
||||
* @brief placeholder for type-driven allocation statistics
|
||||
*
|
||||
* Passed to @ref Object::deep_move for example
|
||||
**/
|
||||
class ObjectStatistics {
|
||||
};
|
||||
|
||||
/** @class PerGenerationStatistics
|
||||
* @brief garbage collection statistics for particular GC generation
|
||||
**/
|
||||
|
|
@ -103,9 +96,6 @@ namespace xo {
|
|||
* (N0 -> N1 when reported; cumulative across GCs)
|
||||
**/
|
||||
std::size_t n_xckp_mutation_ = 0;
|
||||
|
||||
/** per-type statistics (placeholder) **/
|
||||
ObjectStatistics per_type_stats_;
|
||||
};
|
||||
|
||||
inline std::ostream & operator<< (std::ostream & os, const GcStatistics & x) {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "IAlloc.hpp"
|
||||
#include "ObjectStatistics.hpp"
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <cstdint>
|
||||
|
|
@ -43,6 +44,14 @@ namespace xo {
|
|||
/** current free pointer **/
|
||||
std::byte * free_ptr() const;
|
||||
|
||||
/** scan space (must not contain forwarding pointers, because loses size info)
|
||||
* + gather stats by object type
|
||||
*
|
||||
* See @ref Object::self_tp
|
||||
**/
|
||||
void capture_object_statistics(capture_phase phase,
|
||||
ObjectStatistics * p_dest) const;
|
||||
|
||||
// inherited from IAlloc..
|
||||
|
||||
virtual const std::string & name() const final override;
|
||||
|
|
|
|||
|
|
@ -157,6 +157,9 @@ namespace xo {
|
|||
**/
|
||||
virtual TaggedPtr self_tp() const = 0;
|
||||
|
||||
/** print on stream @p os **/
|
||||
virtual void display(std::ostream & os) const = 0;
|
||||
|
||||
// GC support
|
||||
|
||||
/** true iff this object represents a forwarding pointer.
|
||||
|
|
@ -244,6 +247,9 @@ namespace xo {
|
|||
rhs.ptr());
|
||||
}
|
||||
|
||||
std::ostream &
|
||||
operator<< (std::ostream & os, gp<Object> x);
|
||||
|
||||
/** @class Cpof
|
||||
* @brief argument to operator new used for garbage collector evacuation phase
|
||||
*
|
||||
|
|
|
|||
86
xo-alloc/include/xo/alloc/ObjectStatistics.hpp
Normal file
86
xo-alloc/include/xo/alloc/ObjectStatistics.hpp
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
/* file ObjectStatistics.hpp
|
||||
*
|
||||
* author: Roland Conybeare, Aug 2025
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "xo/indentlog/print/pretty.hpp"
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
|
||||
namespace xo {
|
||||
namespace reflect { class TypeDescrBase; }
|
||||
|
||||
namespace gc {
|
||||
enum class capture_phase {
|
||||
/** snapshot-at-beginning **/
|
||||
sab,
|
||||
/** snapshot-at-end **/
|
||||
sae,
|
||||
};
|
||||
|
||||
/** @class PerObjectTypeStatistics
|
||||
* @brief statistics for a particular object type
|
||||
*
|
||||
* Gathered for each leaf type descended from xo::obj::Object.
|
||||
* See @ref xo::obj::Object::self_tp
|
||||
*
|
||||
* See @ref GC::capture_object_statistics
|
||||
* (gathers @ref scanned_n_, @ref scanned_z_)
|
||||
**/
|
||||
struct PerObjectTypeStatistics {
|
||||
using TypeDescr = xo::reflect::TypeDescrBase const *;
|
||||
|
||||
void display(std::ostream & os) const;
|
||||
|
||||
/** stats here are for objects of this type **/
|
||||
TypeDescr td_ = nullptr;
|
||||
/** number of objects scanned **/
|
||||
std::size_t scanned_n_ = 0;
|
||||
/** number of bytes scanned **/
|
||||
std::size_t scanned_z_ = 0;
|
||||
/** number of objects surviving **/
|
||||
std::size_t survive_n_ = 0;
|
||||
/** number of bytes from surviving objects **/
|
||||
std::size_t survive_z_ = 0;
|
||||
};
|
||||
|
||||
inline std::ostream & operator<< (std::ostream & os, const PerObjectTypeStatistics & x) {
|
||||
x.display(os);
|
||||
return os;
|
||||
}
|
||||
|
||||
/** @class ObjectStatistics
|
||||
* @brief placeholder for type-driven allocation statistics
|
||||
*
|
||||
* Passed to @ref Object::deep_move for example
|
||||
**/
|
||||
struct ObjectStatistics {
|
||||
void display(std::ostream & os) const;
|
||||
|
||||
/** per-object-type statistics, indexed by TypeId **/
|
||||
std::vector<PerObjectTypeStatistics> per_type_stats_v_;
|
||||
};
|
||||
|
||||
inline std::ostream & operator<< (std::ostream & os, const ObjectStatistics & x) {
|
||||
x.display(os);
|
||||
return os;
|
||||
}
|
||||
|
||||
} /*namespace gc*/
|
||||
|
||||
namespace print {
|
||||
template <>
|
||||
struct ppdetail<xo::gc::PerObjectTypeStatistics> {
|
||||
static bool print_pretty(const ppindentinfo &, const xo::gc::PerObjectTypeStatistics &);
|
||||
};
|
||||
|
||||
template <>
|
||||
struct ppdetail<xo::gc::ObjectStatistics> {
|
||||
static bool print_pretty(const ppindentinfo &, const xo::gc::ObjectStatistics &);
|
||||
};
|
||||
} /*namespace print*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/* end ObjectStatistics.hpp */
|
||||
|
|
@ -4,6 +4,8 @@
|
|||
*/
|
||||
|
||||
#include "ArenaAlloc.hpp"
|
||||
#include "Object.hpp"
|
||||
#include "ObjectStatistics.hpp"
|
||||
#include "xo/indentlog/scope.hpp"
|
||||
#include "xo/indentlog/print/tag.hpp"
|
||||
#include <cassert>
|
||||
|
|
@ -64,6 +66,43 @@ namespace xo {
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
ArenaAlloc::capture_object_statistics(capture_phase phase,
|
||||
ObjectStatistics * p_dest) const
|
||||
{
|
||||
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();
|
||||
std::uint32_t id = tp.td()->id().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();
|
||||
switch (phase) {
|
||||
case capture_phase::sab:
|
||||
++dest.scanned_n_;
|
||||
dest.scanned_z_ += z;
|
||||
break;
|
||||
case capture_phase::sae:
|
||||
++dest.survive_n_;
|
||||
dest.survive_z_ += z;
|
||||
break;
|
||||
}
|
||||
|
||||
p += z;
|
||||
}
|
||||
|
||||
assert(p == free_ptr_);
|
||||
}
|
||||
|
||||
std::size_t
|
||||
ArenaAlloc::size() const {
|
||||
return limit_ - lo_;
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ set(SELF_SRCS
|
|||
ListAlloc.cpp
|
||||
GC.cpp
|
||||
GcStatistics.cpp
|
||||
ObjectStatistics.cpp
|
||||
Object.cpp
|
||||
Forwarding1.cpp
|
||||
)
|
||||
|
|
|
|||
|
|
@ -23,6 +23,12 @@ namespace xo {
|
|||
return Reflect::make_tp(const_cast<Forwarding1*>(this));
|
||||
}
|
||||
|
||||
void
|
||||
Forwarding1::display(std::ostream & os) const
|
||||
{
|
||||
os << "<fwd" << xtag("dest", (void*)dest_.ptr()) << ">";
|
||||
}
|
||||
|
||||
Object *
|
||||
Forwarding1::_offset_destination(Object * src) const
|
||||
{
|
||||
|
|
|
|||
|
|
@ -12,91 +12,6 @@
|
|||
|
||||
namespace xo {
|
||||
namespace gc {
|
||||
void
|
||||
PerGenerationStatistics::include_gc(std::size_t alloc_z,
|
||||
std::size_t before_z,
|
||||
std::size_t after_z,
|
||||
std::size_t promote_z)
|
||||
{
|
||||
this->update_snapshot(after_z);
|
||||
|
||||
new_alloc_z_ += alloc_z;
|
||||
scanned_z_ += before_z;
|
||||
survive_z_ += after_z;
|
||||
promote_z_ += promote_z;
|
||||
}
|
||||
|
||||
void
|
||||
PerGenerationStatistics::update_snapshot(std::size_t after_z)
|
||||
{
|
||||
used_z_ = after_z;
|
||||
}
|
||||
|
||||
void
|
||||
PerGenerationStatistics::display(std::ostream & os) const
|
||||
{
|
||||
os << "<PerGenerationStatistics"
|
||||
<< xtag("used", used_z_)
|
||||
<< xtag("n_gc", n_gc_)
|
||||
<< xtag("new_alloc_z", new_alloc_z_)
|
||||
<< xtag("scanned_z", scanned_z_)
|
||||
<< xtag("survive_z", survive_z_)
|
||||
<< xtag("promote_z", promote_z_)
|
||||
<< ">";
|
||||
}
|
||||
|
||||
void
|
||||
GcStatistics::include_gc(generation upto,
|
||||
std::size_t alloc_z,
|
||||
std::size_t before_z,
|
||||
std::size_t after_z,
|
||||
std::size_t promote_z)
|
||||
{
|
||||
gen_v_[static_cast<std::size_t>(upto)].include_gc(alloc_z, before_z, after_z, promote_z);
|
||||
}
|
||||
|
||||
void
|
||||
GcStatistics::update_snapshot(generation upto,
|
||||
std::size_t after_z)
|
||||
{
|
||||
gen_v_[static_cast<std::size_t>(upto)].update_snapshot(after_z);
|
||||
}
|
||||
|
||||
void
|
||||
GcStatistics::display(std::ostream & os) const
|
||||
{
|
||||
os << "<GcStatistics"
|
||||
<< xtag("gen_v", gen_v_)
|
||||
<< xtag("total_allocated", total_allocated_)
|
||||
<< xtag("total_promoted_sab", total_promoted_sab_)
|
||||
// total_promoted
|
||||
// n_mtuation
|
||||
// n_logged_mutation
|
||||
// n_xgen_mutation
|
||||
// n_xckp_mutation
|
||||
// << xtag("per_type_stats", per_type_stats_)
|
||||
<< ">";
|
||||
}
|
||||
|
||||
void
|
||||
GcStatisticsExt::display(std::ostream & os) const
|
||||
{
|
||||
os << "<GcStatisticsExt"
|
||||
<< xtag("gen_v", gen_v_)
|
||||
<< xtag("total_allocated", total_allocated_)
|
||||
<< xtag("total_promoted_sab", total_promoted_)
|
||||
<< xtag("nursery_z", nursery_z_)
|
||||
<< xtag("nursery_before_ckp_z", nursery_before_checkpoint_z_)
|
||||
<< xtag("nursery_after_ckp_z", nursery_after_checkpoint_z_)
|
||||
<< xtag("tenured_z", tenured_z_)
|
||||
<< xtag("n_mutation", n_mutation_)
|
||||
<< xtag("n_logged_mutation", n_logged_mutation_)
|
||||
<< xtag("n_xgen_mutation", n_xgen_mutation_)
|
||||
<< xtag("n_xkcp_mutation", n_xckp_mutation_)
|
||||
// << xtag("per_type_stats", per_type_stats_)
|
||||
<< ">";
|
||||
}
|
||||
|
||||
bool
|
||||
MutationLogEntry::is_child_forwarded() const
|
||||
{
|
||||
|
|
@ -542,6 +457,22 @@ namespace xo {
|
|||
|
||||
} /*swap_spaces*/
|
||||
|
||||
void
|
||||
GC::capture_object_statistics(generation upto, capture_phase phase)
|
||||
{
|
||||
/* scan nursery */
|
||||
this->nursery_[role2int(role::to_space)]->capture_object_statistics
|
||||
(phase,
|
||||
&object_statistics_sab_[gen2int(generation::nursery)]);
|
||||
|
||||
if (upto == generation::tenured) {
|
||||
/* scan tenured */
|
||||
this->tenured_[role2int(role::to_space)]->capture_object_statistics
|
||||
(phase,
|
||||
&object_statistics_sab_[gen2int(generation::tenured)]);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GC::copy_object(Object ** pp_object, generation upto, ObjectStatistics * object_stats)
|
||||
{
|
||||
|
|
@ -566,7 +497,7 @@ namespace xo {
|
|||
GC::copy_globals(generation upto)
|
||||
{
|
||||
for (Object ** pp_root : gc_root_v_) {
|
||||
this->copy_object(pp_root, upto, &gc_statistics_.per_type_stats_);
|
||||
this->copy_object(pp_root, upto, &object_statistics_sae_[gen2int(upto)]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -802,7 +733,7 @@ namespace xo {
|
|||
if (upto == generation::tenured) {
|
||||
log && log("TODO: forward mutation log for full GC");
|
||||
} else {
|
||||
this->incremental_gc_forward_mlog(&gc_statistics_.per_type_stats_);
|
||||
this->incremental_gc_forward_mlog(&object_statistics_sae_[gen2int(generation::nursery)]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -858,7 +789,7 @@ namespace xo {
|
|||
void
|
||||
GC::execute_gc(generation upto)
|
||||
{
|
||||
scope log(XO_DEBUG(config_.debug_flag_));
|
||||
scope log(XO_DEBUG(config_.stats_flag_));
|
||||
|
||||
bool full_move = (upto == generation::tenured);
|
||||
|
||||
|
|
@ -876,7 +807,11 @@ namespace xo {
|
|||
|
||||
log && log(xtag("new_alloc", new_alloc));
|
||||
|
||||
log && log("step 1: swap to/from roles");
|
||||
log && log("step 0: (optional) scan for object statistics");
|
||||
|
||||
this->capture_object_statistics(upto, capture_phase::sab);
|
||||
|
||||
log && log("step 1 : swap to/from roles");
|
||||
|
||||
this->swap_spaces(upto);
|
||||
|
||||
|
|
@ -886,18 +821,25 @@ 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);
|
||||
|
||||
this->capture_object_statistics(upto, capture_phase::sae);
|
||||
|
||||
log && log("object statistics [nursery]:");
|
||||
log && log(refrtag("stats", object_statistics_sab_[gen2int(generation::nursery)]));
|
||||
log && log("object statistics [tenured]:");
|
||||
log && log(refrtag("stats", object_statistics_sab_[gen2int(generation::tenured)]));
|
||||
|
||||
this->runstate_ = GCRunstate();
|
||||
|
||||
log && log("statistics:");
|
||||
|
|
|
|||
|
|
@ -7,6 +7,93 @@
|
|||
#include "xo/indentlog/print/pretty_vector.hpp"
|
||||
|
||||
namespace xo {
|
||||
namespace gc {
|
||||
void
|
||||
PerGenerationStatistics::include_gc(std::size_t alloc_z,
|
||||
std::size_t before_z,
|
||||
std::size_t after_z,
|
||||
std::size_t promote_z)
|
||||
{
|
||||
this->update_snapshot(after_z);
|
||||
|
||||
new_alloc_z_ += alloc_z;
|
||||
scanned_z_ += before_z;
|
||||
survive_z_ += after_z;
|
||||
promote_z_ += promote_z;
|
||||
}
|
||||
|
||||
void
|
||||
PerGenerationStatistics::update_snapshot(std::size_t after_z)
|
||||
{
|
||||
used_z_ = after_z;
|
||||
}
|
||||
|
||||
void
|
||||
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_)
|
||||
<< ">";
|
||||
}
|
||||
|
||||
void
|
||||
GcStatistics::include_gc(generation upto,
|
||||
std::size_t alloc_z,
|
||||
std::size_t before_z,
|
||||
std::size_t after_z,
|
||||
std::size_t promote_z)
|
||||
{
|
||||
gen_v_[static_cast<std::size_t>(upto)].include_gc(alloc_z, before_z, after_z, promote_z);
|
||||
}
|
||||
|
||||
void
|
||||
GcStatistics::update_snapshot(generation upto,
|
||||
std::size_t after_z)
|
||||
{
|
||||
gen_v_[static_cast<std::size_t>(upto)].update_snapshot(after_z);
|
||||
}
|
||||
|
||||
void
|
||||
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_)
|
||||
// total_promoted
|
||||
// n_mtuation
|
||||
// n_logged_mutation
|
||||
// n_xgen_mutation
|
||||
// n_xckp_mutation
|
||||
// << xtag("per_type_stats", per_type_stats_)
|
||||
<< ">";
|
||||
}
|
||||
|
||||
void
|
||||
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_)
|
||||
// << xtag("per_type_stats", per_type_stats_)
|
||||
<< ">";
|
||||
}
|
||||
} /*namespace gc*/
|
||||
|
||||
namespace print {
|
||||
bool
|
||||
ppdetail<xo::gc::PerGenerationStatistics>::print_pretty(const ppindentinfo & ppii,
|
||||
|
|
|
|||
|
|
@ -50,6 +50,17 @@ namespace xo {
|
|||
return retval;
|
||||
}
|
||||
|
||||
void
|
||||
ListAlloc::capture_object_statistics(capture_phase phase,
|
||||
ObjectStatistics * p_dest) const
|
||||
{
|
||||
hd_->capture_object_statistics(phase, p_dest);
|
||||
|
||||
for (const auto & arena : full_l_)
|
||||
arena->capture_object_statistics(phase, p_dest);
|
||||
|
||||
}
|
||||
|
||||
const std::string &
|
||||
ListAlloc::name() const {
|
||||
if (hd_) {
|
||||
|
|
|
|||
|
|
@ -191,6 +191,18 @@ namespace xo {
|
|||
(void)fwd;
|
||||
}
|
||||
|
||||
std::ostream &
|
||||
operator<< (std::ostream & os, gp<Object> x)
|
||||
{
|
||||
if (x.ptr()) {
|
||||
x->display(os);
|
||||
} else {
|
||||
os << "<nullptr>";
|
||||
}
|
||||
|
||||
return os;
|
||||
}
|
||||
|
||||
} /*namespace xo*/
|
||||
|
||||
/* end Object.cpp*/
|
||||
|
|
|
|||
73
xo-alloc/src/alloc/ObjectStatistics.cpp
Normal file
73
xo-alloc/src/alloc/ObjectStatistics.cpp
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
/* file ObjectStatistics.cpp
|
||||
*
|
||||
* author: Roland Conybeare, Aug 2025
|
||||
*/
|
||||
|
||||
#include "ObjectStatistics.hpp"
|
||||
#include "xo/reflect/TypeDescr.hpp"
|
||||
#include "xo/indentlog/print/pretty_vector.hpp"
|
||||
|
||||
namespace xo {
|
||||
namespace gc {
|
||||
void
|
||||
PerObjectTypeStatistics::display(std::ostream & os) const
|
||||
{
|
||||
os << "<PerObjectTypeStatistics";
|
||||
if (td_)
|
||||
os << rtag("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_)
|
||||
<< ">";
|
||||
}
|
||||
|
||||
void
|
||||
ObjectStatistics::display(std::ostream & os) const
|
||||
{
|
||||
os << "<ObjectStatistics";
|
||||
|
||||
std::size_t i = 0;
|
||||
for (const auto & x : per_type_stats_v_) {
|
||||
os << " :[" << i << "] " << x;
|
||||
}
|
||||
|
||||
os << ">";
|
||||
}
|
||||
} /*namespace gc*/
|
||||
|
||||
namespace print {
|
||||
bool
|
||||
ppdetail<xo::gc::PerObjectTypeStatistics>::print_pretty(const ppindentinfo & ppii,
|
||||
const xo::gc::PerObjectTypeStatistics & x)
|
||||
{
|
||||
static constexpr std::string_view c_nullptr_str = "nullptr";
|
||||
|
||||
if (x.td_) {
|
||||
return ppii.pps()->pretty_struct(ppii,
|
||||
"PerObjectTypeStatistics",
|
||||
refrtag("td", x.td_ ? x.td_->short_name() : c_nullptr_str),
|
||||
refrtag("scanned_n", x.scanned_n_),
|
||||
refrtag("scanned_z", x.scanned_z_),
|
||||
refrtag("survive_n", x.survive_n_),
|
||||
refrtag("survive_z", x.survive_z_));
|
||||
} else {
|
||||
/* print nothing -- empty struct */
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
ppdetail<xo::gc::ObjectStatistics>::print_pretty(const ppindentinfo & ppii,
|
||||
const xo::gc::ObjectStatistics & x)
|
||||
{
|
||||
return ppii.pps()->pretty_struct(ppii,
|
||||
"ObjectTypeStatistics",
|
||||
refrtag("per_type_stats_v", x.per_type_stats_v_));
|
||||
}
|
||||
} /*namespace gc*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/* end ObjectStatistics.cpp */
|
||||
|
|
@ -1,87 +0,0 @@
|
|||
/* @file LinearAlloc.test.cpp
|
||||
*
|
||||
* author: Roland Conybeare, Jul 2025
|
||||
*/
|
||||
|
||||
#include "xo/alloc/ArenaAlloc.hpp"
|
||||
#include <catch2/catch.hpp>
|
||||
|
||||
namespace xo {
|
||||
using xo::gc::LinearAlloc;
|
||||
|
||||
namespace ut {
|
||||
|
||||
namespace {
|
||||
struct testcase_alloc {
|
||||
testcase_alloc(std::size_t rz, std::size_t z)
|
||||
: redline_z_{rz}, arena_z_{z} {}
|
||||
|
||||
std::size_t redline_z_;
|
||||
std::size_t arena_z_;
|
||||
|
||||
};
|
||||
|
||||
std::vector<testcase_alloc>
|
||||
s_testcase_v = {
|
||||
testcase_alloc(0, 4096)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE("linearalloc", "[alloc]")
|
||||
{
|
||||
for (std::size_t i_tc = 0, n_tc = s_testcase_v.size(); i_tc < n_tc; ++i_tc) {
|
||||
const testcase_alloc & tc = s_testcase_v[i_tc];
|
||||
|
||||
constexpr bool c_debug_flag = false;
|
||||
|
||||
auto alloc = LinearAlloc::make("linearalloc", tc.redline_z_, tc.arena_z_, c_debug_flag);
|
||||
|
||||
REQUIRE(alloc.get());
|
||||
REQUIRE(alloc->name() == "linearalloc");
|
||||
REQUIRE(alloc->size() == tc.arena_z_);
|
||||
REQUIRE(alloc->available() == tc.arena_z_);
|
||||
REQUIRE(alloc->allocated() == 0);
|
||||
REQUIRE(alloc->is_before_checkpoint(alloc->free_ptr()) == false);
|
||||
REQUIRE(alloc->before_checkpoint() == 0);
|
||||
REQUIRE(alloc->after_checkpoint() == 0);
|
||||
|
||||
auto free0 = alloc->free_ptr();
|
||||
|
||||
auto mem = alloc->alloc(tc.arena_z_);
|
||||
|
||||
REQUIRE(mem != nullptr);
|
||||
|
||||
REQUIRE(mem == free0);
|
||||
|
||||
REQUIRE(alloc->size() == tc.arena_z_);
|
||||
REQUIRE(alloc->available() == 0);
|
||||
REQUIRE(alloc->allocated() == tc.arena_z_);
|
||||
REQUIRE(alloc->is_before_checkpoint(mem) == false);
|
||||
REQUIRE(alloc->before_checkpoint() == 0);
|
||||
REQUIRE(alloc->after_checkpoint() == tc.arena_z_);
|
||||
|
||||
alloc->clear();
|
||||
|
||||
REQUIRE(alloc->free_ptr() == free0);
|
||||
REQUIRE(alloc->available() == tc.arena_z_);
|
||||
REQUIRE(alloc->allocated() == 0);
|
||||
REQUIRE(alloc->is_before_checkpoint(free0) == false);
|
||||
REQUIRE(alloc->before_checkpoint() == 0);
|
||||
REQUIRE(alloc->after_checkpoint() == 0);
|
||||
|
||||
mem = alloc->alloc(1);
|
||||
|
||||
auto used = sizeof(void*);
|
||||
REQUIRE(alloc->size() == tc.arena_z_);
|
||||
REQUIRE(alloc->available() == tc.arena_z_ - used);
|
||||
REQUIRE(alloc->allocated() == used);
|
||||
REQUIRE(alloc->is_before_checkpoint(free0) == false);
|
||||
REQUIRE(alloc->before_checkpoint() == 0);
|
||||
REQUIRE(alloc->after_checkpoint() == used);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
} /*namespace ut */
|
||||
} /*namespace xo*/
|
||||
|
|
@ -34,15 +34,38 @@ namespace xo {
|
|||
} else {
|
||||
ppii.pps()->write('[');
|
||||
|
||||
for (size_t i = 0, z = x.size(); i < z; ++i) {
|
||||
if (i == 0)
|
||||
ppii.pps()->indent(std::max(ppii.pps()->indent_width(), 1u) - 1);
|
||||
else
|
||||
ppii.pps()->newline_indent(ppii.ci1());
|
||||
ppii.pps()->pretty(x[i]);
|
||||
bool skip_next_indent = false;
|
||||
|
||||
if (i+1 < z)
|
||||
ppii.pps()->write(',');
|
||||
for (size_t i = 0, z = x.size(); i < z; ++i) {
|
||||
if (!skip_next_indent) {
|
||||
if (i == 0)
|
||||
ppii.pps()->indent(std::max(ppii.pps()->indent_width(), 1u) - 1);
|
||||
else
|
||||
ppii.pps()->newline_indent(ppii.ci1());
|
||||
}
|
||||
|
||||
std::uint32_t pos0 = ppii.pps()->pos();
|
||||
ppii.pps()->pretty(x[i]);
|
||||
std::uint32_t pos1 = ppii.pps()->pos();
|
||||
|
||||
bool skip_prev_indent = skip_next_indent;
|
||||
|
||||
/* special case, did not print anything */
|
||||
skip_next_indent = (pos0 == pos1);
|
||||
|
||||
if (skip_next_indent) {
|
||||
if (skip_prev_indent) {
|
||||
/* compress consecutive empty items */
|
||||
;
|
||||
} else {
|
||||
/* catch edge from non-empty -> empty */
|
||||
ppii.pps()->write(",.. ,");
|
||||
ppii.pps()->newline_indent(ppii.ci1());
|
||||
}
|
||||
} else {
|
||||
if (i+1 < z)
|
||||
ppii.pps()->write(',');
|
||||
}
|
||||
}
|
||||
|
||||
ppii.pps()->write(" ]");
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ namespace xo {
|
|||
// inherited from Object..
|
||||
|
||||
virtual TaggedPtr self_tp() const final override;
|
||||
virtual void display(std::ostream & os) const final override;
|
||||
virtual std::size_t _shallow_size() const final override;
|
||||
virtual Object * _shallow_copy() const final override;
|
||||
virtual std::size_t _forward_children() final override;
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ namespace xo {
|
|||
|
||||
// inherited from Object..
|
||||
virtual TaggedPtr self_tp() const final override;
|
||||
virtual void display(std::ostream & os) const final override;
|
||||
virtual std::size_t _shallow_size() const final override;
|
||||
virtual Object * _shallow_copy() const final override;
|
||||
virtual std::size_t _forward_children() final override;
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ namespace xo {
|
|||
// inherited from Object..
|
||||
|
||||
virtual TaggedPtr self_tp() const final override;
|
||||
virtual void display(std::ostream & os) const final override;
|
||||
virtual std::size_t _shallow_size() const final override;
|
||||
virtual Object * _shallow_copy() const final override;
|
||||
virtual std::size_t _forward_children() final override;
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ namespace xo {
|
|||
|
||||
// inherited from Object..
|
||||
virtual TaggedPtr self_tp() const final override;
|
||||
virtual void display(std::ostream & os) const final override;
|
||||
virtual std::size_t _shallow_size() const final override;
|
||||
virtual Object * _shallow_copy() const final override;
|
||||
virtual std::size_t _forward_children() final override;
|
||||
|
|
|
|||
|
|
@ -42,6 +42,12 @@ namespace xo {
|
|||
return Reflect::make_tp(const_cast<Boolean*>(this));
|
||||
}
|
||||
|
||||
void
|
||||
Boolean::display(std::ostream & os) const
|
||||
{
|
||||
os << (value_ ? "#t" : "#f");
|
||||
}
|
||||
|
||||
std::size_t
|
||||
Boolean::_shallow_size() const
|
||||
{
|
||||
|
|
|
|||
|
|
@ -29,6 +29,11 @@ namespace xo {
|
|||
return Reflect::make_tp(const_cast<Integer*>(this));
|
||||
}
|
||||
|
||||
void
|
||||
Integer::display(std::ostream & os) const {
|
||||
os << value_;
|
||||
}
|
||||
|
||||
std::size_t
|
||||
Integer::_shallow_size() const {
|
||||
return sizeof(Integer);
|
||||
|
|
|
|||
|
|
@ -75,6 +75,18 @@ namespace xo {
|
|||
return Reflect::make_tp(const_cast<List*>(this));
|
||||
}
|
||||
|
||||
void
|
||||
List::display(std::ostream & os) const {
|
||||
gp<List> l = const_cast<List*>(this);
|
||||
|
||||
os << "(";
|
||||
while (!l->is_nil()) {
|
||||
os << l->head();
|
||||
l = l->rest();
|
||||
}
|
||||
os << ")";
|
||||
}
|
||||
|
||||
std::size_t
|
||||
List::_shallow_size() const {
|
||||
return sizeof(List);
|
||||
|
|
|
|||
|
|
@ -96,6 +96,13 @@ namespace xo {
|
|||
return Reflect::make_tp(const_cast<String*>(this));
|
||||
}
|
||||
|
||||
void
|
||||
String::display(std::ostream & os) const {
|
||||
// TODO: print with escapes
|
||||
|
||||
os << "\"" << c_str() << "\"";
|
||||
}
|
||||
|
||||
// ----- GC support -----
|
||||
|
||||
std::size_t
|
||||
|
|
|
|||
|
|
@ -251,7 +251,7 @@ namespace xo {
|
|||
ObjectGraphModel::verify_equal_models(ObjectGraphModel & from_model,
|
||||
ObjectGraphModel & to_model)
|
||||
{
|
||||
REQUIRE(from_model.roots_.size() == to_model.nodes_.size());
|
||||
REQUIRE(from_model.roots_.size() == to_model.roots_.size());
|
||||
REQUIRE(from_model.nodes_.size() == to_model.nodes_.size());
|
||||
|
||||
std::unordered_set<std::uintptr_t> visited_set;
|
||||
|
|
@ -288,7 +288,7 @@ namespace xo {
|
|||
std::size_t new_index = this->nodes_.size();
|
||||
{
|
||||
if (x_int.is_null() && x_list.is_null())
|
||||
throw std::runtime_error("expecting object graph containing int|cons only");
|
||||
throw std::runtime_error(tostr("expecting object graph containing int|cons|nil only", xtag("x", x)));
|
||||
|
||||
if (!x_int.is_null()) {
|
||||
new_model.index_ = new_index;
|
||||
|
|
@ -402,6 +402,9 @@ namespace xo {
|
|||
|
||||
void RandomMutationModel::generate_seed_values()
|
||||
{
|
||||
w1_.clear();
|
||||
w2_.clear();
|
||||
|
||||
{
|
||||
for (size_t i = 0; i < m_; ++i) {
|
||||
w1_.push_back(List::cons(List::nil, List::nil));
|
||||
|
|
@ -443,10 +446,12 @@ namespace xo {
|
|||
for (std::size_t i = 0; i < k_; ++i) {
|
||||
/* pick a root list cell at random */
|
||||
gp<List> l1 = w1_.at((*p_rgen)() % w1_.size());
|
||||
REQUIRE(l1.ptr());
|
||||
|
||||
if ((*p_rgen)() % 2 == 0) {
|
||||
/* pick another root list cell at random, and link it to l1 */
|
||||
gp<List> l2 = w1_.at((*p_rgen)() % w1_.size());
|
||||
REQUIRE(l2.ptr());
|
||||
|
||||
l1->assign_rest(l2);
|
||||
} else {
|
||||
|
|
@ -454,6 +459,7 @@ namespace xo {
|
|||
* assign to head
|
||||
*/
|
||||
gp<Object> x2 = w2_.at((*p_rgen)() % w2_.size());
|
||||
REQUIRE(x2.ptr());
|
||||
|
||||
l1->assign_head(x2);
|
||||
}
|
||||
|
|
@ -463,6 +469,8 @@ namespace xo {
|
|||
void RandomMutationModel::rejuvenate_seed_values()
|
||||
{
|
||||
for (std::size_t i = 0; i < w1_.size(); ++i) {
|
||||
INFO(xtag("i", i));
|
||||
|
||||
if (w1_.at(i)->_is_forwarded()) {
|
||||
/* w[i] survived GC */
|
||||
w1_[i] = dynamic_cast<List *>(w1_[i]->_destination());
|
||||
|
|
@ -470,15 +478,20 @@ namespace xo {
|
|||
/* w[i] is garbage, replace */
|
||||
w1_[i] = List::cons(List::nil, List::nil);
|
||||
}
|
||||
REQUIRE(w1_[i].ptr());
|
||||
}
|
||||
|
||||
for (std::size_t j = 0; j < w2_.size(); ++j) {
|
||||
INFO(xtag("j", j));
|
||||
|
||||
if (w2_.at(j)->_is_forwarded()) {
|
||||
/* w2[i] survived GC */
|
||||
w2_[j] = dynamic_cast<Integer *>(w2_[j]->_destination());
|
||||
w2_[j] = w2_[j]->_destination();
|
||||
REQUIRE(w2_[j].ptr());
|
||||
} else {
|
||||
/* w2[j] is garbage, replace */
|
||||
w2_[j] = Integer::make((this->start_)++);
|
||||
REQUIRE(w2_[j].ptr());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -534,8 +547,17 @@ namespace xo {
|
|||
|
||||
std::vector<testcase_stresstest> s_testcase_v =
|
||||
{
|
||||
/* nz tz m n r rr k stats, debug */
|
||||
testcase_stresstest(1024, 1024, 3, 7, 5, 2, 10, true, false)
|
||||
/* nz: nursery size
|
||||
* tz: tenured size
|
||||
* m: #of random list cells to create
|
||||
* n: #of random integers to create
|
||||
* r: #of gc roots to create
|
||||
* rr: #of gc roots to replace between iterations
|
||||
* k: #of random mutations to apply
|
||||
*
|
||||
* nz tz m n r rr k stats, debug */
|
||||
testcase_stresstest(1024, 1024, 5, 1, 5, 2, 10, true, false),
|
||||
testcase_stresstest(1024, 1024, 10, 10, 5, 2, 10, true, false)
|
||||
};
|
||||
} /*namespace*/
|
||||
|
||||
|
|
@ -550,6 +572,7 @@ namespace xo {
|
|||
{
|
||||
.initial_nursery_z_ = tc.nursery_z_,
|
||||
.initial_tenured_z_ = tc.tenured_z_,
|
||||
.stats_flag_ = tc.gc_stats_flag_,
|
||||
.debug_flag_ = tc.debug_flag_
|
||||
});
|
||||
|
||||
|
|
@ -582,7 +605,9 @@ namespace xo {
|
|||
|
||||
RandomMutationModel data_model(tc.m_, tc.n_, tc.r_, tc.rr_, tc.k_);
|
||||
|
||||
for (std::size_t cycle = 0; cycle < 2; ++cycle) {
|
||||
for (std::size_t cycle = 0; cycle < 5; ++cycle) {
|
||||
INFO(xtag("cycle", cycle));
|
||||
|
||||
if (cycle == 0) {
|
||||
data_model.generate_seed_values();
|
||||
data_model.generate_random_roots(gc.get(), &rgen);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue