xo-object: improve GC unittest + prep to integrate w/ xo::reflect
This commit is contained in:
parent
66e2b07ce5
commit
ea60d107e8
13 changed files with 545 additions and 235 deletions
|
|
@ -6,6 +6,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "ListAlloc.hpp"
|
||||
#include "GcStatistics.hpp"
|
||||
#include "xo/indentlog/print/array.hpp"
|
||||
#include <vector>
|
||||
#include <array>
|
||||
|
|
@ -15,20 +16,6 @@ namespace xo {
|
|||
class Object;
|
||||
|
||||
namespace gc {
|
||||
enum class generation {
|
||||
nursery,
|
||||
tenured,
|
||||
N
|
||||
};
|
||||
|
||||
constexpr std::size_t gen2int(generation x) { return static_cast<std::size_t>(x); }
|
||||
|
||||
enum class generation_result {
|
||||
nursery,
|
||||
tenured,
|
||||
not_found
|
||||
};
|
||||
|
||||
enum class role {
|
||||
/** nursery: generation for new objects **/
|
||||
from_space,
|
||||
|
|
@ -59,104 +46,6 @@ namespace xo {
|
|||
bool debug_flag_ = false;
|
||||
};
|
||||
|
||||
/** @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
|
||||
**/
|
||||
class PerGenerationStatistics {
|
||||
public:
|
||||
/** update statistics after a GC cycle
|
||||
* @param alloc_z. new allocations (since preceding GC)
|
||||
* @param before_z. generation size (bytes allocated) before collection
|
||||
* @param after_z. generation size after collection
|
||||
* @param promote_z. bytes promoted to next generation
|
||||
**/
|
||||
void include_gc(std::size_t alloc_z, std::size_t before_z, std::size_t after_z,
|
||||
std::size_t promote_z);
|
||||
/** update with current state (use at end of gc cycle) **/
|
||||
void update_snapshot(std::size_t after_z);
|
||||
|
||||
/** @param os. write stats on this output stream **/
|
||||
void display(std::ostream & os) const;
|
||||
|
||||
/** number of bytes currently in use **/
|
||||
std::size_t used_z_ = 0;
|
||||
|
||||
/** number of collection cycles completed **/
|
||||
std::size_t n_gc_ = 0;
|
||||
/** sum of new alloc bytes, sampled at start of each collection cycle **/
|
||||
std::size_t new_alloc_z_ = 0;
|
||||
/** sum of allocated bytes sampled at beginning of each collection cycle **/
|
||||
std::size_t scanned_z_ = 0;
|
||||
/** sum of bytes remaining after collection cycle **/
|
||||
std::size_t survive_z_ = 0;
|
||||
/** sum of bytes promoted to next generation **/
|
||||
std::size_t promote_z_ = 0;
|
||||
};
|
||||
|
||||
inline std::ostream & operator<< (std::ostream & os, const PerGenerationStatistics & x) {
|
||||
x.display(os);
|
||||
return os;
|
||||
}
|
||||
|
||||
/** @class GcStatistics
|
||||
* @brief garbage collection statistics
|
||||
**/
|
||||
class GcStatistics {
|
||||
public:
|
||||
/** update statistics after a GC cycle
|
||||
* @param upto. nursery -> incremental collection; tenured -> full collection
|
||||
* @param alloc_z. new allocations (since preceding GC)
|
||||
* @param before_z. generation size (bytes allocated) before collection
|
||||
* @param after_z. generation size after collection
|
||||
* @param promote_z. bytes promoted to next generation
|
||||
**/
|
||||
void include_gc(generation upto, std::size_t alloc_z,
|
||||
std::size_t before_z, std::size_t after_z, std::size_t promote_z);
|
||||
/** update snapshot for current state.
|
||||
* Use with tenured stats after incremental gc
|
||||
**/
|
||||
void update_snapshot(generation upto, std::size_t after_z);
|
||||
|
||||
/** @param os. write stats on this output stream **/
|
||||
void display(std::ostream & os) const;
|
||||
|
||||
/** statistics gathered across {incr, full} GCs respectively **/
|
||||
std::array<PerGenerationStatistics, static_cast<std::size_t>(generation::N)> gen_v_;
|
||||
/** total bytes allocated since inception **/
|
||||
std::size_t total_allocated_ = 0;
|
||||
/** snapshot of total bytes promoted asof beginning of last gc cycle **/
|
||||
std::size_t total_promoted_sab_ = 0;
|
||||
/** total bytes promoted from nursery->tenured since inception **/
|
||||
std::size_t total_promoted_ = 0;
|
||||
|
||||
/** total number of mutations to already-allocated objects,
|
||||
* whether or not GC needs to log them.
|
||||
**/
|
||||
std::size_t n_mutation_ = 0;
|
||||
/** total number of mutation eligible for logging **/
|
||||
std::size_t n_logged_mutation_ = 0;
|
||||
/** total number of cross-generation mutations (tenured->nursery when reported) **/
|
||||
std::size_t n_xgen_mutation_ = 0;
|
||||
/** total number of cross-checkpoint mutations (N0 -> N1 when reported) **/
|
||||
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) {
|
||||
x.display(os);
|
||||
return os;
|
||||
}
|
||||
|
||||
/** @class GCRunstate
|
||||
* @brief encapsulate state needed while GC is running
|
||||
*
|
||||
|
|
@ -228,7 +117,8 @@ namespace xo {
|
|||
static up<GC> make(const Config & config);
|
||||
|
||||
const GCRunstate & runstate() const { return runstate_; }
|
||||
const GcStatistics & gc_statistics() const { return gc_statistics_; }
|
||||
const GcStatistics & native_gc_statistics() const { return gc_statistics_; }
|
||||
GcStatisticsExt get_gc_statistics() const;
|
||||
|
||||
/** true iff GC permitted in current state **/
|
||||
bool is_gc_enabled() const { return gc_enabled_ == 0; }
|
||||
|
|
|
|||
161
xo-alloc/include/xo/alloc/GcStatistics.hpp
Normal file
161
xo-alloc/include/xo/alloc/GcStatistics.hpp
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
/* GcStatistics.hpp
|
||||
*
|
||||
* author: Roland Conybeare, Aug 2025
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "generation.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
|
||||
**/
|
||||
class PerGenerationStatistics {
|
||||
public:
|
||||
/** update statistics after a GC cycle
|
||||
* @param alloc_z. new allocations (since preceding GC)
|
||||
* @param before_z. generation size (bytes allocated) before collection
|
||||
* @param after_z. generation size after collection
|
||||
* @param promote_z. bytes promoted to next generation
|
||||
**/
|
||||
void include_gc(std::size_t alloc_z, std::size_t before_z, std::size_t after_z,
|
||||
std::size_t promote_z);
|
||||
/** update with current state (use at end of gc cycle) **/
|
||||
void update_snapshot(std::size_t after_z);
|
||||
|
||||
/** @param os. write stats on this output stream **/
|
||||
void display(std::ostream & os) const;
|
||||
|
||||
/** number of bytes currently in use **/
|
||||
std::size_t used_z_ = 0;
|
||||
|
||||
/** number of collection cycles completed **/
|
||||
std::size_t n_gc_ = 0;
|
||||
/** sum of new alloc bytes, sampled at start of each collection cycle **/
|
||||
std::size_t new_alloc_z_ = 0;
|
||||
/** sum of allocated bytes sampled at beginning of each collection cycle **/
|
||||
std::size_t scanned_z_ = 0;
|
||||
/** sum of bytes remaining after collection cycle **/
|
||||
std::size_t survive_z_ = 0;
|
||||
/** sum of bytes promoted to next generation **/
|
||||
std::size_t promote_z_ = 0;
|
||||
};
|
||||
|
||||
inline std::ostream & operator<< (std::ostream & os, const PerGenerationStatistics & x) {
|
||||
x.display(os);
|
||||
return os;
|
||||
}
|
||||
|
||||
/** @class GcStatistics
|
||||
* @brief garbage collection statistics
|
||||
**/
|
||||
class GcStatistics {
|
||||
public:
|
||||
/** update statistics after a GC cycle
|
||||
* @param upto. nursery -> incremental collection; tenured -> full collection
|
||||
* @param alloc_z. new allocations (since preceding GC)
|
||||
* @param before_z. generation size (bytes allocated) before collection
|
||||
* @param after_z. generation size after collection
|
||||
* @param promote_z. bytes promoted to next generation
|
||||
**/
|
||||
void include_gc(generation upto, std::size_t alloc_z,
|
||||
std::size_t before_z, std::size_t after_z, std::size_t promote_z);
|
||||
/** update snapshot for current state.
|
||||
* Use with tenured stats after incremental gc
|
||||
**/
|
||||
void update_snapshot(generation upto, std::size_t after_z);
|
||||
|
||||
/** @param os. write stats on this output stream **/
|
||||
void display(std::ostream & os) const;
|
||||
|
||||
/** statistics gathered across {incr, full} GCs respectively **/
|
||||
std::array<PerGenerationStatistics, static_cast<std::size_t>(generation::N)> gen_v_;
|
||||
/** total bytes allocated since inception **/
|
||||
std::size_t total_allocated_ = 0;
|
||||
/** snapshot of total bytes promoted asof beginning of last gc cycle **/
|
||||
std::size_t total_promoted_sab_ = 0;
|
||||
/** total bytes promoted from nursery->tenured since inception **/
|
||||
std::size_t total_promoted_ = 0;
|
||||
|
||||
/** total number of mutations to already-allocated objects,
|
||||
* whether or not GC needs to log them.
|
||||
**/
|
||||
std::size_t n_mutation_ = 0;
|
||||
/** total number of mutation eligible for logging (cumulative across GCs) **/
|
||||
std::size_t n_logged_mutation_ = 0;
|
||||
/** total number of cross-generation mutations
|
||||
* (tenured->nursery when reported; cumulative across GCs) **/
|
||||
std::size_t n_xgen_mutation_ = 0;
|
||||
/** total number of cross-checkpoint mutations
|
||||
* (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) {
|
||||
x.display(os);
|
||||
return os;
|
||||
}
|
||||
|
||||
/** @class GcStatisticsExt
|
||||
* @brief extend GcStatistics for application convenience
|
||||
**/
|
||||
class GcStatisticsExt : public GcStatistics {
|
||||
public:
|
||||
explicit GcStatisticsExt(const GcStatistics & x) : GcStatistics{x} {}
|
||||
|
||||
/** @param os. write stats on this output stream **/
|
||||
void display(std::ostream & os) const;
|
||||
|
||||
/** current capacity of nursery generation **/
|
||||
std::size_t nursery_z_ = 0;
|
||||
/** current nursery survivor size **/
|
||||
std::size_t nursery_before_checkpoint_z_ = 0;
|
||||
/** current nursery new alloc size **/
|
||||
std::size_t nursery_after_checkpoint_z_ = 0;
|
||||
/** current capacity of tenured generation **/
|
||||
std::size_t tenured_z_ = 0;
|
||||
};
|
||||
|
||||
inline std::ostream & operator<< (std::ostream & os, const GcStatisticsExt & x) {
|
||||
x.display(os);
|
||||
return os;
|
||||
}
|
||||
|
||||
} /*namespace gc*/
|
||||
|
||||
namespace print {
|
||||
template <>
|
||||
struct ppdetail<xo::gc::PerGenerationStatistics> {
|
||||
static bool print_pretty(const ppindentinfo &, const xo::gc::PerGenerationStatistics &);
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ppdetail<xo::gc::GcStatistics> {
|
||||
static bool print_pretty(const ppindentinfo &, const xo::gc::GcStatistics &);
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ppdetail<xo::gc::GcStatisticsExt> {
|
||||
static bool print_pretty(const ppindentinfo &, const xo::gc::GcStatisticsExt &);
|
||||
};
|
||||
} /*namespace print*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/* end GcStatistics.hpp */
|
||||
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "xo/reflect/SelfTagging.hpp"
|
||||
#include "IAlloc.hpp"
|
||||
#include <concepts>
|
||||
#include <cstdint>
|
||||
|
|
|
|||
26
xo-alloc/include/xo/alloc/generation.hpp
Normal file
26
xo-alloc/include/xo/alloc/generation.hpp
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
/* generation.hpp
|
||||
*
|
||||
* author: Roland Conybeare, Aug 2025
|
||||
*/
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace xo {
|
||||
namespace gc {
|
||||
enum class generation {
|
||||
nursery,
|
||||
tenured,
|
||||
N
|
||||
};
|
||||
|
||||
constexpr std::size_t gen2int(generation x) { return static_cast<std::size_t>(x); }
|
||||
|
||||
enum class generation_result {
|
||||
nursery,
|
||||
tenured,
|
||||
not_found
|
||||
};
|
||||
} /*namespace gc*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/* end generation.hpp */
|
||||
|
|
@ -6,11 +6,12 @@ set(SELF_SRCS
|
|||
ArenaAlloc.cpp
|
||||
ListAlloc.cpp
|
||||
GC.cpp
|
||||
GcStatistics.cpp
|
||||
Object.cpp
|
||||
Forwarding1.cpp
|
||||
)
|
||||
|
||||
xo_add_shared_library4(${SELF_LIB} ${PROJECT_NAME}Targets ${PROJECT_VERSION} 1 ${SELF_SRCS})
|
||||
xo_dependency(${SELF_LIB} indentlog)
|
||||
xo_dependency(${SELF_LIB} reflect)
|
||||
|
||||
#end CMakeLists.txt
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
* author: Roland Conybeare, Jul 2025
|
||||
*/
|
||||
|
||||
#include "GcStatistics.hpp"
|
||||
#include "GC.hpp"
|
||||
#include "Object.hpp"
|
||||
#include "xo/indentlog/scope.hpp"
|
||||
|
|
@ -67,6 +68,31 @@ namespace xo {
|
|||
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_)
|
||||
<< ">";
|
||||
}
|
||||
|
|
@ -228,6 +254,18 @@ namespace xo {
|
|||
return config_.debug_flag_;
|
||||
}
|
||||
|
||||
GcStatisticsExt
|
||||
GC::get_gc_statistics() const
|
||||
{
|
||||
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.tenured_z_ = tenured_[role2int(role::to_space)]->size();
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
generation_result
|
||||
GC::fromspace_generation_of(const void * x) const
|
||||
{
|
||||
|
|
|
|||
68
xo-alloc/src/alloc/GcStatistics.cpp
Normal file
68
xo-alloc/src/alloc/GcStatistics.cpp
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
/* GcStatistics.cpp
|
||||
*
|
||||
* author: Roland Conybeare, Aug 2025
|
||||
*/
|
||||
|
||||
#include "GcStatistics.hpp"
|
||||
#include "xo/indentlog/print/pretty_vector.hpp"
|
||||
|
||||
namespace xo {
|
||||
namespace print {
|
||||
bool
|
||||
ppdetail<xo::gc::PerGenerationStatistics>::print_pretty(const ppindentinfo & ppii,
|
||||
const xo::gc::PerGenerationStatistics & x)
|
||||
{
|
||||
return ppii.pps()->pretty_struct(ppii,
|
||||
"PerGenerationStatistics",
|
||||
refrtag("used_z", x.used_z_),
|
||||
refrtag("n_gc", x.n_gc_),
|
||||
refrtag("new_alloc_z", x.new_alloc_z_),
|
||||
refrtag("scanned_z", x.scanned_z_),
|
||||
refrtag("survive_z", x.survive_z_),
|
||||
refrtag("promote_z", x.promote_z_)
|
||||
);
|
||||
}
|
||||
|
||||
bool
|
||||
ppdetail<xo::gc::GcStatistics>::print_pretty(const ppindentinfo & ppii,
|
||||
const xo::gc::GcStatistics & x)
|
||||
{
|
||||
return ppii.pps()->pretty_struct(ppii,
|
||||
"GcStatistics",
|
||||
refrtag("gen_v", x.gen_v_),
|
||||
refrtag("total_allocated", x.total_allocated_),
|
||||
refrtag("total_promoted_sab", x.total_promoted_sab_),
|
||||
refrtag("total_promoted", x.total_promoted_),
|
||||
refrtag("n_mutation", x.n_mutation_),
|
||||
refrtag("n_logged_mutation", x.n_logged_mutation_),
|
||||
refrtag("n_xgen_mutation", x.n_xgen_mutation_),
|
||||
refrtag("n_xckp_mutation", x.n_xckp_mutation_)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
ppdetail<xo::gc::GcStatisticsExt>::print_pretty(const ppindentinfo & ppii,
|
||||
const xo::gc::GcStatisticsExt & x)
|
||||
{
|
||||
return ppii.pps()->pretty_struct(ppii,
|
||||
"GcStatisticsExt",
|
||||
refrtag("gen_v", x.gen_v_),
|
||||
refrtag("total_allocated", x.total_allocated_),
|
||||
refrtag("total_promoted_sab", x.total_promoted_sab_),
|
||||
refrtag("total_promoted", x.total_promoted_),
|
||||
refrtag("n_mutation", x.n_mutation_),
|
||||
refrtag("n_logged_mutation", x.n_logged_mutation_),
|
||||
refrtag("n_xgen_mutation", x.n_xgen_mutation_),
|
||||
refrtag("n_xckp_mutation", x.n_xckp_mutation_),
|
||||
refrtag("nursery_z", x.nursery_z_),
|
||||
refrtag("nursery_before_checkpoint_z", x.nursery_before_checkpoint_z_),
|
||||
refrtag("nursery_after_checkpoint_z", x.nursery_after_checkpoint_z_),
|
||||
refrtag("tenured_z", x.tenured_z_));
|
||||
}
|
||||
|
||||
|
||||
} /*namespace print*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/* end GcStatistics.cpp */
|
||||
|
|
@ -47,22 +47,22 @@ namespace xo {
|
|||
|
||||
REQUIRE(gc->gc_in_progress() == false);
|
||||
REQUIRE(gc->is_gc_enabled() == true);
|
||||
REQUIRE(gc->gc_statistics().gen_v_[gen2int(generation::nursery)].n_gc_ == 0);
|
||||
REQUIRE(gc->gc_statistics().gen_v_[gen2int(generation::tenured)].n_gc_ == 0);
|
||||
REQUIRE(gc->native_gc_statistics().gen_v_[gen2int(generation::nursery)].n_gc_ == 0);
|
||||
REQUIRE(gc->native_gc_statistics().gen_v_[gen2int(generation::tenured)].n_gc_ == 0);
|
||||
|
||||
/* gc with empty state */
|
||||
gc->request_gc(generation::nursery);
|
||||
|
||||
REQUIRE(gc->gc_in_progress() == false);
|
||||
REQUIRE(gc->gc_statistics().gen_v_[gen2int(generation::nursery)].n_gc_ == 1);
|
||||
REQUIRE(gc->gc_statistics().gen_v_[gen2int(generation::tenured)].n_gc_ == 0);
|
||||
REQUIRE(gc->native_gc_statistics().gen_v_[gen2int(generation::nursery)].n_gc_ == 1);
|
||||
REQUIRE(gc->native_gc_statistics().gen_v_[gen2int(generation::tenured)].n_gc_ == 0);
|
||||
|
||||
/* still empty state */
|
||||
gc->request_gc(generation::tenured);
|
||||
|
||||
REQUIRE(gc->gc_in_progress() == false);
|
||||
REQUIRE(gc->gc_statistics().gen_v_[gen2int(generation::nursery)].n_gc_ == 1);
|
||||
REQUIRE(gc->gc_statistics().gen_v_[gen2int(generation::tenured)].n_gc_ == 1);
|
||||
REQUIRE(gc->native_gc_statistics().gen_v_[gen2int(generation::nursery)].n_gc_ == 1);
|
||||
REQUIRE(gc->native_gc_statistics().gen_v_[gen2int(generation::tenured)].n_gc_ == 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ namespace xo {
|
|||
// Defining this means ppdetail_atomic is not used.
|
||||
// In that case where not explicitly specialized ppdetail will revert to ordinary printing for a type,
|
||||
// instead of giving compile-time error about missing template specialization of ppdetail.
|
||||
#define ppdetail_atomic ppdetail
|
||||
//#define ppdetail_atomic ppdetail
|
||||
|
||||
struct ppindentinfo {
|
||||
ppindentinfo(ppstate * pps, std::uint32_t ci0, std::uint32_t indent_width, bool upto)
|
||||
|
|
|
|||
|
|
@ -6,8 +6,11 @@
|
|||
#pragma once
|
||||
|
||||
#include "pretty.hpp"
|
||||
#include "array.hpp" /*printing*/
|
||||
#include "pad.hpp"
|
||||
#include <vector>
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
|
||||
namespace xo {
|
||||
namespace print {
|
||||
|
|
@ -54,5 +57,12 @@ namespace xo {
|
|||
return ppdetail_vector<std::vector<T>>::print_pretty(ppii, x);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
struct ppdetail<std::array<T, N>> {
|
||||
static bool print_pretty(const ppindentinfo & ppii, const std::array<T,N> & x) {
|
||||
return ppdetail_vector<std::array<T, N>>::print_pretty(ppii, x);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,8 +47,8 @@ namespace xo {
|
|||
.debug_flag_ = false
|
||||
});
|
||||
|
||||
REQUIRE(gc->gc_statistics().n_mutation_ == 0);
|
||||
REQUIRE(gc->gc_statistics().n_logged_mutation_ == 0);
|
||||
REQUIRE(gc->native_gc_statistics().n_mutation_ == 0);
|
||||
REQUIRE(gc->native_gc_statistics().n_logged_mutation_ == 0);
|
||||
|
||||
REQUIRE(gc.get());
|
||||
|
||||
|
|
@ -82,10 +82,10 @@ namespace xo {
|
|||
|
||||
l->assign_head(Integer::make(2));
|
||||
{
|
||||
REQUIRE(gc->gc_statistics().n_mutation_ == 1);
|
||||
REQUIRE(gc->gc_statistics().n_logged_mutation_ == 0);
|
||||
REQUIRE(gc->gc_statistics().n_xgen_mutation_ == 0);
|
||||
REQUIRE(gc->gc_statistics().n_xckp_mutation_ == 0);
|
||||
REQUIRE(gc->native_gc_statistics().n_mutation_ == 1);
|
||||
REQUIRE(gc->native_gc_statistics().n_logged_mutation_ == 0);
|
||||
REQUIRE(gc->native_gc_statistics().n_xgen_mutation_ == 0);
|
||||
REQUIRE(gc->native_gc_statistics().n_xckp_mutation_ == 0);
|
||||
REQUIRE(gc->mlog_size() == 0);
|
||||
|
||||
REQUIRE(gc->is_gc_enabled() == true);
|
||||
|
|
@ -113,10 +113,10 @@ namespace xo {
|
|||
REQUIRE(gc->tospace_generation_of(l->head().ptr()) == generation_result::nursery);
|
||||
REQUIRE(gc->is_before_checkpoint(l->head().ptr()) == false);
|
||||
|
||||
REQUIRE(gc->gc_statistics().n_mutation_ == 2);
|
||||
REQUIRE(gc->gc_statistics().n_logged_mutation_ == 1);
|
||||
REQUIRE(gc->gc_statistics().n_xgen_mutation_ == 0);
|
||||
REQUIRE(gc->gc_statistics().n_xckp_mutation_ == 1);
|
||||
REQUIRE(gc->native_gc_statistics().n_mutation_ == 2);
|
||||
REQUIRE(gc->native_gc_statistics().n_logged_mutation_ == 1);
|
||||
REQUIRE(gc->native_gc_statistics().n_xgen_mutation_ == 0);
|
||||
REQUIRE(gc->native_gc_statistics().n_xckp_mutation_ == 1);
|
||||
REQUIRE(gc->mlog_size() == 1);
|
||||
}
|
||||
|
||||
|
|
@ -132,12 +132,12 @@ namespace xo {
|
|||
REQUIRE(gc->tospace_generation_of(l->head().ptr()) == generation_result::nursery);
|
||||
REQUIRE(gc->is_before_checkpoint(l->head().ptr()));
|
||||
|
||||
REQUIRE(gc->gc_statistics().n_mutation_ == 2);
|
||||
REQUIRE(gc->gc_statistics().n_logged_mutation_ == 1);
|
||||
REQUIRE(gc->native_gc_statistics().n_mutation_ == 2);
|
||||
REQUIRE(gc->native_gc_statistics().n_logged_mutation_ == 1);
|
||||
// counters recorded when mutation created.
|
||||
// not modified by gc
|
||||
REQUIRE(gc->gc_statistics().n_xgen_mutation_ == 0);
|
||||
REQUIRE(gc->gc_statistics().n_xckp_mutation_ == 1);
|
||||
REQUIRE(gc->native_gc_statistics().n_xgen_mutation_ == 0);
|
||||
REQUIRE(gc->native_gc_statistics().n_xckp_mutation_ == 1);
|
||||
REQUIRE(gc->mlog_size() == 1);
|
||||
}
|
||||
|
||||
|
|
@ -356,9 +356,162 @@ namespace xo {
|
|||
this->roots_.push_back(traverse_from_object(x));
|
||||
}
|
||||
|
||||
/** Generate some random data + mutations to verify GC behavior
|
||||
*
|
||||
* To setup for first GC:
|
||||
* RandomMutationModel model(m, n, r, k);
|
||||
* model.generate_seed_values();
|
||||
* model.generate_random_roots(gc, &rgen);
|
||||
* model.generate_random_mutations(&rgen);
|
||||
*
|
||||
* To prepare for next GC
|
||||
* model.rejuvenate_seed_values();
|
||||
* model.alter_random_roots(&rgen);
|
||||
* model.generate_random_mutations(&rgen);
|
||||
**/
|
||||
struct RandomMutationModel {
|
||||
RandomMutationModel(std::size_t m, std::size_t n, std::size_t r, std::size_t rr, std::size_t k)
|
||||
: m_{m}, n_{n}, r_{r}, rr_{rr}, k_{k} {}
|
||||
|
||||
void generate_seed_values();
|
||||
void generate_random_roots(GC * gc, xoshiro256ss * p_rgen);
|
||||
void generate_random_mutations(xoshiro256ss * p_rgen);
|
||||
|
||||
void rejuvenate_seed_values();
|
||||
void alter_random_roots(xoshiro256ss * p_rgen);
|
||||
|
||||
/* create m random list cells */
|
||||
size_t m_ = 0;
|
||||
/** create n random integers, starting with value @ref start_ **/
|
||||
size_t start_ = 0;
|
||||
size_t n_ = 0;
|
||||
/* #of roots */
|
||||
size_t r_ = 0;
|
||||
size_t rr_ = 0;
|
||||
/* #of random mutations */
|
||||
size_t k_ = 0;
|
||||
|
||||
/* w1[] contains some random list cells */
|
||||
std::vector<gp<List>> w1_;
|
||||
/* w2[] has all of w1[], also contains some integers */
|
||||
std::vector<gp<Object>> w2_;
|
||||
|
||||
/* create some random roots. always pick at least one list cell */
|
||||
std::vector<gp<Object>> root_v_;
|
||||
};
|
||||
|
||||
void RandomMutationModel::generate_seed_values()
|
||||
{
|
||||
{
|
||||
for (size_t i = 0; i < m_; ++i) {
|
||||
w1_.push_back(List::cons(List::nil, List::nil));
|
||||
}
|
||||
REQUIRE(w1_.size() == m_);
|
||||
}
|
||||
|
||||
{
|
||||
std::copy(w1_.begin(), w1_.end(), std::back_inserter(w2_));
|
||||
for (size_t j = 0; j < n_; ++j) {
|
||||
w2_.push_back(Integer::make((this->start_)++));
|
||||
}
|
||||
REQUIRE(w2_.size() == m_ + n_);
|
||||
}
|
||||
}
|
||||
|
||||
void RandomMutationModel::generate_random_roots(GC * gc,
|
||||
xoshiro256ss * p_rgen)
|
||||
{
|
||||
std::size_t w1_ix = (*p_rgen)() % n_;
|
||||
{
|
||||
root_v_.push_back(w2_.at(w1_ix));
|
||||
for (std::size_t i = 1; i < r_; ++i) {
|
||||
std::size_t w2_ix = (*p_rgen)() % (m_ + n_);
|
||||
|
||||
root_v_.push_back(w2_.at(w2_ix));
|
||||
}
|
||||
|
||||
REQUIRE(root_v_.size() == r_);
|
||||
|
||||
for (auto & root : root_v_)
|
||||
gc->add_gc_root(root.ptr_address());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void RandomMutationModel::generate_random_mutations(xoshiro256ss * p_rgen)
|
||||
{
|
||||
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());
|
||||
|
||||
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());
|
||||
|
||||
l1->assign_rest(l2);
|
||||
} else {
|
||||
/* pick a value at random (could be list or integer),
|
||||
* assign to head
|
||||
*/
|
||||
gp<Object> x2 = w2_.at((*p_rgen)() % w2_.size());
|
||||
|
||||
l1->assign_head(x2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RandomMutationModel::rejuvenate_seed_values()
|
||||
{
|
||||
for (std::size_t i = 0; i < w1_.size(); ++i) {
|
||||
if (w1_.at(i)->_is_forwarded()) {
|
||||
/* w[i] survived GC */
|
||||
w1_[i] = dynamic_cast<List *>(w1_[i]->_destination());
|
||||
} else {
|
||||
/* w[i] is garbage, replace */
|
||||
w1_[i] = List::cons(List::nil, List::nil);
|
||||
}
|
||||
}
|
||||
|
||||
for (std::size_t j = 0; j < w2_.size(); ++j) {
|
||||
if (w2_.at(j)->_is_forwarded()) {
|
||||
/* w2[i] survived GC */
|
||||
w2_[j] = dynamic_cast<Integer *>(w2_[j]->_destination());
|
||||
} else {
|
||||
/* w2[j] is garbage, replace */
|
||||
w2_[j] = Integer::make((this->start_)++);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RandomMutationModel::alter_random_roots(xoshiro256ss * p_rgen)
|
||||
{
|
||||
/* replace a root value rr times */
|
||||
for (std::size_t i = 0; i < rr_; ++i) {
|
||||
/* choose new root value at random */
|
||||
gp<Object> new_root;
|
||||
{
|
||||
std::size_t j = (*p_rgen)() % (w1_.size() + w2_.size());
|
||||
|
||||
if (j < w1_.size())
|
||||
new_root = w1_.at(j);
|
||||
else
|
||||
new_root = w2_.at(j - w1_.size());
|
||||
}
|
||||
|
||||
/* choose a root to replace at random */
|
||||
std::size_t j = (*p_rgen)() % root_v_.size();
|
||||
|
||||
root_v_[j] = new_root;
|
||||
}
|
||||
}
|
||||
|
||||
struct testcase_stresstest {
|
||||
testcase_stresstest(std::size_t nz, std::size_t tz, std::size_t m, std::size_t n, std::size_t r, std::size_t k, bool debug_flag)
|
||||
: nursery_z_{nz}, tenured_z_{tz}, m_{m}, n_{n}, r_{r}, k_{k}, debug_flag_{debug_flag}
|
||||
testcase_stresstest(std::size_t nz, std::size_t tz,
|
||||
std::size_t m, std::size_t n,
|
||||
std::size_t r, std::size_t rr, std::size_t k,
|
||||
bool gc_stats_flag, bool debug_flag)
|
||||
: nursery_z_{nz}, tenured_z_{tz}, m_{m}, n_{n}, r_{r}, rr_{rr}, k_{k},
|
||||
gc_stats_flag_{gc_stats_flag}, debug_flag_{debug_flag}
|
||||
{}
|
||||
|
||||
std::size_t nursery_z_;
|
||||
|
|
@ -370,15 +523,19 @@ namespace xo {
|
|||
std::size_t n_;
|
||||
/* #of gc roots to create */
|
||||
std::size_t r_;
|
||||
/* #of gc roots to replace between cycles */
|
||||
std::size_t rr_;
|
||||
/* #of random mutations */
|
||||
std::size_t k_;
|
||||
|
||||
bool debug_flag_;
|
||||
bool gc_stats_flag_ = false;
|
||||
bool debug_flag_ = false;
|
||||
};
|
||||
|
||||
std::vector<testcase_stresstest> s_testcase_v =
|
||||
{
|
||||
testcase_stresstest(1024, 1024, 3, 7, 5, 10, true)
|
||||
/* nz tz m n r rr k stats, debug */
|
||||
testcase_stresstest(1024, 1024, 3, 7, 5, 2, 10, true, false)
|
||||
};
|
||||
} /*namespace*/
|
||||
|
||||
|
|
@ -387,7 +544,7 @@ namespace xo {
|
|||
for (std::size_t i_tc = 0, n_tc = s_testcase_v.size(); i_tc < n_tc; ++i_tc) {
|
||||
const testcase_stresstest & tc = s_testcase_v[i_tc];
|
||||
|
||||
scope log(XO_DEBUG(tc.debug_flag_));
|
||||
scope log(XO_DEBUG(tc.gc_stats_flag_));
|
||||
|
||||
up<GC> gc = GC::make(
|
||||
{
|
||||
|
|
@ -396,8 +553,8 @@ namespace xo {
|
|||
.debug_flag_ = tc.debug_flag_
|
||||
});
|
||||
|
||||
REQUIRE(gc->gc_statistics().n_mutation_ == 0);
|
||||
REQUIRE(gc->gc_statistics().n_logged_mutation_ == 0);
|
||||
REQUIRE(gc->native_gc_statistics().n_mutation_ == 0);
|
||||
REQUIRE(gc->native_gc_statistics().n_logged_mutation_ == 0);
|
||||
|
||||
REQUIRE(gc.get());
|
||||
|
||||
|
|
@ -419,92 +576,50 @@ namespace xo {
|
|||
//std::cerr << "seed=" << seed << std::endl;
|
||||
auto rgen = xoshiro256ss(seed);
|
||||
|
||||
/* create m random list cells */
|
||||
size_t m = tc.m_;
|
||||
/* create n random integers */
|
||||
size_t n = tc.n_;
|
||||
/* #of roots */
|
||||
size_t r = tc.r_;
|
||||
/* #of random mutations */
|
||||
size_t k = tc.k_;
|
||||
REQUIRE(tc.m_ > 0);
|
||||
REQUIRE(tc.n_ > 0);
|
||||
REQUIRE(tc.r_ > 0);
|
||||
|
||||
REQUIRE(m > 0);
|
||||
REQUIRE(n > 0);
|
||||
RandomMutationModel data_model(tc.m_, tc.n_, tc.r_, tc.rr_, tc.k_);
|
||||
|
||||
/* w1[] contains some random list cells */
|
||||
std::vector<gp<List>> w1;
|
||||
{
|
||||
for (size_t i = 0; i < m; ++i) {
|
||||
w1.push_back(List::cons(List::nil, List::nil));
|
||||
}
|
||||
REQUIRE(w1.size() == m);
|
||||
}
|
||||
|
||||
/* w2[] has all of w1[], also contains some integers */
|
||||
std::vector<gp<Object>> w2;
|
||||
{
|
||||
std::copy(w1.begin(), w1.end(), std::back_inserter(w2));
|
||||
for (size_t j = 0; j < n; ++j) {
|
||||
w2.push_back(Integer::make(j));
|
||||
}
|
||||
REQUIRE(w2.size() == m + n);
|
||||
}
|
||||
|
||||
/* create some random roots. always pick at least one list cell */
|
||||
std::vector<gp<Object>> root_v;
|
||||
std::size_t w1_ix = rgen() % n;
|
||||
{
|
||||
root_v.push_back(w2.at(w1_ix));
|
||||
for (std::size_t i = 1; i < r; ++i) {
|
||||
std::size_t w2_ix = rgen() % (m + n);
|
||||
|
||||
root_v.push_back(w2.at(w2_ix));
|
||||
for (std::size_t cycle = 0; cycle < 2; ++cycle) {
|
||||
if (cycle == 0) {
|
||||
data_model.generate_seed_values();
|
||||
data_model.generate_random_roots(gc.get(), &rgen);
|
||||
} else {
|
||||
/* figure out values in {data_model_.w1_, data_model_.w2_} that
|
||||
* survived GC; keep these. Discard the remainder.
|
||||
* don't want these as roots, because that would alter the behavior of GC.
|
||||
*
|
||||
* (For example want to verify behavior of GC w.r.t. cells that are alive only
|
||||
* because of a mutation)
|
||||
*/
|
||||
data_model.rejuvenate_seed_values();
|
||||
data_model.alter_random_roots(&rgen);
|
||||
}
|
||||
|
||||
for (auto & root : root_v)
|
||||
gc->add_gc_root(root.ptr_address());
|
||||
data_model.generate_random_mutations(&rgen);
|
||||
|
||||
log && log(xtag("cycle", cycle),
|
||||
xtag("stats.before", gc->get_gc_statistics()));
|
||||
|
||||
/* make model for contents of w2[] - baseline for post-GC comparison */
|
||||
ObjectGraphModel from_model;
|
||||
from_model.from_root_vector(data_model.root_v_);
|
||||
|
||||
gc->request_gc(generation::nursery);
|
||||
|
||||
/* collector cycle changed object addresses.
|
||||
* build a new object model, and verify consistency with from_model
|
||||
*/
|
||||
ObjectGraphModel to_model;
|
||||
to_model.from_root_vector(data_model.root_v_);
|
||||
|
||||
REQUIRE(ObjectGraphModel::verify_equal_models(from_model, to_model));
|
||||
|
||||
log && log(xtag("cycle", cycle),
|
||||
xtag("stats.after", gc->get_gc_statistics()));
|
||||
}
|
||||
|
||||
/* random mutations -- these will get logged */
|
||||
{
|
||||
for (std::size_t i = 0; i < k; ++i) {
|
||||
/* pick a list cell at random */
|
||||
gp<List> l1 = w1.at(rgen() % w1.size());
|
||||
|
||||
if (rgen() % 2 == 0) {
|
||||
/* pick another list cell at random, and link it to l1 */
|
||||
gp<List> l2 = w1.at(rgen() % w1.size());
|
||||
|
||||
l1->assign_rest(l2);
|
||||
} else {
|
||||
/* pick a value at random (could be list or integer),
|
||||
* assign to head
|
||||
*/
|
||||
gp<Object> x2 = w2.at(rgen() % w2.size());
|
||||
|
||||
l1->assign_head(x2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log && log("stats.before", gc->gc_statistics());
|
||||
|
||||
/* make model for contents of w2[] */
|
||||
ObjectGraphModel from_model;
|
||||
from_model.from_root_vector(root_v);
|
||||
|
||||
gc->request_gc(generation::nursery);
|
||||
|
||||
/* collector cycle changed object addresses.
|
||||
* build a new object model, and verify that they're equivalent
|
||||
*/
|
||||
|
||||
ObjectGraphModel to_model;
|
||||
to_model.from_root_vector(root_v);
|
||||
|
||||
REQUIRE(ObjectGraphModel::verify_equal_models(from_model, to_model));
|
||||
|
||||
log && log("stats.after", gc->gc_statistics());
|
||||
}
|
||||
}
|
||||
} /*namespace ut*/
|
||||
|
|
|
|||
|
|
@ -103,8 +103,8 @@ namespace xo {
|
|||
*/
|
||||
gc->request_gc(generation::nursery);
|
||||
|
||||
REQUIRE(gc->gc_statistics().gen_v_[gen2int(generation::nursery)].n_gc_ == 1);
|
||||
REQUIRE(gc->gc_statistics().gen_v_[gen2int(generation::tenured)].n_gc_ == 0);
|
||||
REQUIRE(gc->native_gc_statistics().gen_v_[gen2int(generation::nursery)].n_gc_ == 1);
|
||||
REQUIRE(gc->native_gc_statistics().gen_v_[gen2int(generation::tenured)].n_gc_ == 0);
|
||||
REQUIRE(gc->allocated() == expected_alloc_z);
|
||||
|
||||
/* verify GC preserved list structure and contents */
|
||||
|
|
@ -128,8 +128,8 @@ namespace xo {
|
|||
/* every has survived one GC cycle. collect again should promote */
|
||||
gc->request_gc(generation::nursery);
|
||||
|
||||
REQUIRE(gc->gc_statistics().gen_v_[gen2int(generation::nursery)].n_gc_ == 2);
|
||||
REQUIRE(gc->gc_statistics().gen_v_[gen2int(generation::tenured)].n_gc_ == 0);
|
||||
REQUIRE(gc->native_gc_statistics().gen_v_[gen2int(generation::nursery)].n_gc_ == 2);
|
||||
REQUIRE(gc->native_gc_statistics().gen_v_[gen2int(generation::tenured)].n_gc_ == 0);
|
||||
REQUIRE(gc->allocated() == expected_alloc_z);
|
||||
|
||||
/* verify GC preserved list structure and contents */
|
||||
|
|
@ -150,12 +150,12 @@ namespace xo {
|
|||
}
|
||||
}
|
||||
|
||||
REQUIRE(gc->gc_statistics().total_promoted_ == gc->allocated());
|
||||
REQUIRE(gc->native_gc_statistics().total_promoted_ == gc->allocated());
|
||||
|
||||
gc->request_gc(generation::tenured);
|
||||
|
||||
REQUIRE(gc->gc_statistics().gen_v_[gen2int(generation::nursery)].n_gc_ == 2);
|
||||
REQUIRE(gc->gc_statistics().gen_v_[gen2int(generation::tenured)].n_gc_ == 1);
|
||||
REQUIRE(gc->native_gc_statistics().gen_v_[gen2int(generation::nursery)].n_gc_ == 2);
|
||||
REQUIRE(gc->native_gc_statistics().gen_v_[gen2int(generation::tenured)].n_gc_ == 1);
|
||||
REQUIRE(gc->allocated() == expected_alloc_z);
|
||||
|
||||
/* verify GC preserved list structure and contents */
|
||||
|
|
@ -176,7 +176,7 @@ namespace xo {
|
|||
}
|
||||
}
|
||||
|
||||
log && log("stats", gc->gc_statistics());
|
||||
log && log("stats", gc->native_gc_statistics());
|
||||
}
|
||||
}
|
||||
} /*TEST_CASE(List, ..)*/
|
||||
|
|
@ -250,8 +250,8 @@ namespace xo {
|
|||
|
||||
gc->request_gc(generation::nursery);
|
||||
|
||||
REQUIRE(gc->gc_statistics().gen_v_[gen2int(generation::nursery)].n_gc_ == 1);
|
||||
REQUIRE(gc->gc_statistics().gen_v_[gen2int(generation::tenured)].n_gc_ == 0);
|
||||
REQUIRE(gc->native_gc_statistics().gen_v_[gen2int(generation::nursery)].n_gc_ == 1);
|
||||
REQUIRE(gc->native_gc_statistics().gen_v_[gen2int(generation::tenured)].n_gc_ == 0);
|
||||
REQUIRE(gc->allocated() == expected_alloc_z);
|
||||
|
||||
/* verify GC preserved list structure and contents */
|
||||
|
|
|
|||
|
|
@ -79,10 +79,10 @@ namespace xo {
|
|||
gc->request_gc(generation::nursery);
|
||||
|
||||
REQUIRE(gc->gc_in_progress() == false);
|
||||
REQUIRE(gc->gc_statistics().gen_v_[gen2int(generation::nursery)].n_gc_ == 1);
|
||||
REQUIRE(gc->gc_statistics().gen_v_[gen2int(generation::tenured)].n_gc_ == 0);
|
||||
REQUIRE(gc->native_gc_statistics().gen_v_[gen2int(generation::nursery)].n_gc_ == 1);
|
||||
REQUIRE(gc->native_gc_statistics().gen_v_[gen2int(generation::tenured)].n_gc_ == 0);
|
||||
REQUIRE(gc->allocated() == 0);
|
||||
REQUIRE(gc->gc_statistics().total_allocated_ == expected_alloc_z);
|
||||
REQUIRE(gc->native_gc_statistics().total_allocated_ == expected_alloc_z);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -140,8 +140,8 @@ namespace xo {
|
|||
|
||||
/* gc has a bunch of string objects; all are roots + should be preserved */
|
||||
gc->request_gc(generation::nursery);
|
||||
REQUIRE(gc->gc_statistics().gen_v_[gen2int(generation::nursery)].n_gc_ == 1);
|
||||
REQUIRE(gc->gc_statistics().gen_v_[gen2int(generation::tenured)].n_gc_ == 0);
|
||||
REQUIRE(gc->native_gc_statistics().gen_v_[gen2int(generation::nursery)].n_gc_ == 1);
|
||||
REQUIRE(gc->native_gc_statistics().gen_v_[gen2int(generation::tenured)].n_gc_ == 0);
|
||||
|
||||
REQUIRE(gc->allocated() == expected_alloc_z);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue