diff --git a/xo-alloc/include/xo/alloc/GC.hpp b/xo-alloc/include/xo/alloc/GC.hpp index ab58b902..7c4f42c6 100644 --- a/xo-alloc/include/xo/alloc/GC.hpp +++ b/xo-alloc/include/xo/alloc/GC.hpp @@ -132,6 +132,7 @@ namespace xo { public: using CallbackId = xo::fn::CallbackId; using GcCopyCallbackSet = xo::fn::UpCallbackSet; + using nanos = decltype(xo::qty::qty::nanosecond); public: /** create new GC instance with configuration @p config **/ @@ -224,6 +225,8 @@ namespace xo { * as number of calls to @ref disable_gc. **/ void enable_gc(); + /** same as @c this->enable_gc() followed by @c this->disable_gc() **/ + void enable_gc_once(); // inherited from IAlloc.. @@ -279,7 +282,7 @@ namespace xo { /** begin GC now **/ void execute_gc(generation g); /** cleanup phase. aux function for @ref execute_gc **/ - void cleanup_phase(generation g); + void cleanup_phase(generation g, nanos dt); /** swap roles of From/To spaces for nursery generation **/ void swap_nursery(); /** swap roles of From/To spaces for tenured generation **/ diff --git a/xo-alloc/include/xo/alloc/GcStatistics.hpp b/xo-alloc/include/xo/alloc/GcStatistics.hpp index d73f8947..d0f54084 100644 --- a/xo-alloc/include/xo/alloc/GcStatistics.hpp +++ b/xo-alloc/include/xo/alloc/GcStatistics.hpp @@ -8,6 +8,8 @@ #include "generation.hpp" #include "CircularBuffer.hpp" #include "xo/reflect/TypeDescr.hpp" +#include "xo/unit/quantity.hpp" +#include "xo/unit/quantity_iostream.hpp" #include "xo/indentlog/print/pretty.hpp" #include #include @@ -60,6 +62,13 @@ namespace xo { public: GcStatistics() = default; + /** update statistics at beginning of a GC cycle + * @param upto. nursery -> incremental collection; tenured -> full collection + * @param alloc_z. new allocations (since preceding GC) + **/ + void begin_gc(generation upto, + std::size_t alloc_z); + /** update statistics after a GC cycle * @param upto. nursery -> incremental collection; tenured -> full collection * @param alloc_z. new allocations (since preceding GC) @@ -74,6 +83,9 @@ namespace xo { **/ void update_snapshot(generation upto, std::size_t after_z); + /** number of collection cycles, whether full or incremental **/ + std::size_t n_gc() const { return gen_v_[gen2int(generation::nursery)].n_gc_; } + /** @param os. write stats on this output stream **/ void display(std::ostream & os) const; @@ -136,31 +148,54 @@ namespace xo { * @brief info we want to record over time (won't have cumulative things in it) **/ class GcStatisticsHistoryItem { + public: + using nanos = xo::qty::type::nanoseconds; + public: GcStatisticsHistoryItem() = default; - GcStatisticsHistoryItem(generation upto, - std::size_t new_alloc_z, - std::size_t survive_z, - std::size_t promote_z, - std::size_t persist_z, - std::size_t effort_z, - std::size_t garbage0_z, - std::size_t garbage1_z, - std::size_t garbageN_z) - : upto_{upto}, - new_alloc_z_{new_alloc_z}, - survive_z_{survive_z}, - promote_z_{promote_z}, - persist_z_{persist_z}, - effort_z_{effort_z}, - garbage0_z_{garbage0_z}, - garbage1_z_{garbage1_z}, - garbageN_z_{garbageN_z} - {} + constexpr GcStatisticsHistoryItem(std::size_t gc_seq, + generation upto, + std::size_t new_alloc_z, + std::size_t survive_z, + std::size_t promote_z, + std::size_t persist_z, + std::size_t effort_z, + std::size_t garbage0_z, + std::size_t garbage1_z, + std::size_t garbageN_z, + nanos dt) : gc_seq_{gc_seq}, + upto_{upto}, + new_alloc_z_{new_alloc_z}, + survive_z_{survive_z}, + promote_z_{promote_z}, + persist_z_{persist_z}, + effort_z_{effort_z}, + garbage0_z_{garbage0_z}, + garbage1_z_{garbage1_z}, + garbageN_z_{garbageN_z}, + dt_{dt} {} + constexpr GcStatisticsHistoryItem(const GcStatisticsHistoryItem &) = default; + + GcStatisticsHistoryItem & operator=(const GcStatisticsHistoryItem & x) { + gc_seq_ = x.gc_seq_; + upto_ = x.upto_; + new_alloc_z_ = x.new_alloc_z_; + survive_z_ = x.survive_z_; + promote_z_ = x.promote_z_; + persist_z_ = x.persist_z_; + effort_z_ = x.effort_z_; + garbage0_z_ = x.garbage0_z_; + garbage1_z_ = x.garbage1_z_; + garbageN_z_ = x.garbageN_z_; + this->dt_.scale_ = x.dt_.scale_; + return *this; + } /** @param os. write stats on this output stream **/ void display(std::ostream & os) const; + /** sequence number for collection being reported **/ + std::size_t gc_seq_ = 0; /** type of GC that generated this record **/ generation upto_; /** #of bytes new allocation **/ @@ -181,6 +216,8 @@ namespace xo { std::size_t garbage1_z_ = 0; /** #of bytes garbage from T (i.e. survived 2+ GCs) **/ std::size_t garbageN_z_ = 0; + /** elapsed time for this GC (see @ref GC::execute_gc) **/ + nanos dt_; }; inline std::ostream & operator<< (std::ostream & os, const GcStatisticsHistoryItem & x) { diff --git a/xo-alloc/src/alloc/CMakeLists.txt b/xo-alloc/src/alloc/CMakeLists.txt index 07f87784..b83e2618 100644 --- a/xo-alloc/src/alloc/CMakeLists.txt +++ b/xo-alloc/src/alloc/CMakeLists.txt @@ -14,6 +14,7 @@ set(SELF_SRCS ) xo_add_shared_library4(${SELF_LIB} ${PROJECT_NAME}Targets ${PROJECT_VERSION} 1 ${SELF_SRCS}) +xo_dependency(${SELF_LIB} xo_unit) xo_dependency(${SELF_LIB} reflect) xo_dependency(${SELF_LIB} callback) diff --git a/xo-alloc/src/alloc/GC.cpp b/xo-alloc/src/alloc/GC.cpp index a552ae1e..e6fb188f 100644 --- a/xo-alloc/src/alloc/GC.cpp +++ b/xo-alloc/src/alloc/GC.cpp @@ -7,6 +7,7 @@ #include "GC.hpp" #include "Object.hpp" #include "xo/indentlog/scope.hpp" +#include #include #include @@ -1037,9 +1038,9 @@ namespace xo { } void - GC::cleanup_phase(generation upto) + GC::cleanup_phase(generation upto, nanos dt) { - scope log(XO_DEBUG(config_.debug_flag_)); + scope log(XO_DEBUG(config_.stats_flag_)); std::size_t N0_before_gc = nursery_from()->after_checkpoint(); std::size_t N1_before_gc = nursery_from()->before_checkpoint(); @@ -1098,6 +1099,7 @@ namespace xo { this->tenured_to()->checkpoint(); if (log) { + log(xtag("gcseq_before_gc", gc_statistics_.n_gc())); log(xtag("N0_before_gc", N0_before_gc)); log(xtag("N1_before_gc", N1_before_gc)); log(xtag("N_after_gc", N_after_gc)); @@ -1107,18 +1109,6 @@ namespace xo { log(xtag("T_after_gc", T_after_gc)); } - GcStatisticsHistoryItem item(upto, - new_alloc_z, - survive_z, - promote_z, - persist_z, - effort_z, - garbage0_z, - garbage1_z, - garbageN_z); - - this->gc_history_.push_back(item); - this->incr_gc_pending_ = false; this->gc_statistics_.include_gc(generation::nursery, N0_before_gc, N_before_gc, N_after_gc, promote_z); @@ -1129,6 +1119,23 @@ namespace xo { // still want to update tenured stats for current alloc size this->gc_statistics_.update_snapshot(generation::tenured, T_after_gc); } + GcStatisticsHistoryItem item(gc_statistics_.n_gc(), + upto, + new_alloc_z, + survive_z, + promote_z, + persist_z, + effort_z, + garbage0_z, + garbage1_z, + garbageN_z, + dt); + + log && log(xtag("gcseq_after_gc", gc_statistics_.n_gc()), + xtag("item", item)); + + this->gc_history_.push_back(item); + } /*cleanup_phase*/ void @@ -1136,6 +1143,8 @@ namespace xo { { scope log(XO_DEBUG(config_.stats_flag_)); + auto t0 = std::chrono::steady_clock::now(); + bool full_move = (upto == generation::tenured); // TODO: RAII version in case of exceptions @@ -1146,9 +1155,7 @@ namespace xo { /* new allocation since last GC */ std::size_t new_alloc = this->after_checkpoint(); - ++(gc_statistics_.gen_v_[static_cast(upto)].n_gc_); - gc_statistics_.total_allocated_ += new_alloc; - gc_statistics_.total_promoted_sab_ = gc_statistics_.total_promoted_; + gc_statistics_.begin_gc(upto, new_alloc); log && log(xtag("new_alloc", new_alloc)); @@ -1176,10 +1183,13 @@ namespace xo { log && log("step 6 : cleanup"); - this->cleanup_phase(upto); - this->capture_object_statistics(upto, capture_phase::sae); + auto t1 = std::chrono::steady_clock::now(); + auto dt = std::chrono::duration_cast(t1 - t0); + + this->cleanup_phase(upto, xo::qty::qty::nanoseconds(dt.count())); + log && log("object statistics [nursery]:"); log && log(refrtag("stats", object_statistics_sab_[gen2int(generation::nursery)])); log && log("object statistics [tenured]:"); @@ -1233,6 +1243,13 @@ namespace xo { this->request_gc(full_gc_pending_ ? generation::tenured : generation::nursery); } } + + void + GC::enable_gc_once() { + this->enable_gc(); + this->disable_gc(); + } + } /*namespace gc*/ } /*namespace xo*/ diff --git a/xo-alloc/src/alloc/GcStatistics.cpp b/xo-alloc/src/alloc/GcStatistics.cpp index cb7ebebf..72624403 100644 --- a/xo-alloc/src/alloc/GcStatistics.cpp +++ b/xo-alloc/src/alloc/GcStatistics.cpp @@ -16,6 +16,7 @@ namespace xo { { this->update_snapshot(after_z); + //++n_gc_; new_alloc_z_ += alloc_z; scanned_z_ += before_z; survive_z_ += after_z; @@ -41,6 +42,15 @@ namespace xo { << ">"; } + void + GcStatistics::begin_gc(generation upto, + std::size_t new_alloc) + { + ++(this->gen_v_[static_cast(upto)].n_gc_); + this->total_allocated_ += new_alloc; + this->total_promoted_sab_ = total_promoted_; + } + void GcStatistics::include_gc(generation upto, std::size_t alloc_z, @@ -105,6 +115,7 @@ namespace xo { << xrtag("garbage0_z", garbage0_z_) << xrtag("garbage1_z", garbage1_z_) << xrtag("garbageN_z", garbageN_z_) + << xrtag("dt", dt_) << ">"; } @@ -177,7 +188,8 @@ namespace xo { refrtag("effort_z", x.effort_z_), refrtag("garbage0_z", x.garbage0_z_), refrtag("garbage1_z", x.garbage1_z_), - refrtag("garbageN_z", x.garbageN_z_)); + refrtag("garbageN_z", x.garbageN_z_), + refrtag("dt", x.dt_)); } } /*namespace print*/ } /*namespace xo*/ diff --git a/xo-imgui/example/ex2/CMakeLists.txt b/xo-imgui/example/ex2/CMakeLists.txt index c0671d01..21b633b7 100644 --- a/xo-imgui/example/ex2/CMakeLists.txt +++ b/xo-imgui/example/ex2/CMakeLists.txt @@ -58,5 +58,6 @@ if (XO_ENABLE_EXAMPLES) xo_dependency(${SELF_EXE} xo_object) xo_dependency(${SELF_EXE} randomgen) + xo_dependency(${SELF_EXE} xo_flatstring) xo_dependency(${SELF_EXE} xo_alloc) endif() diff --git a/xo-imgui/example/ex2/imgui_ex2.cpp b/xo-imgui/example/ex2/imgui_ex2.cpp index d8aba0fe..b8456718 100644 --- a/xo-imgui/example/ex2/imgui_ex2.cpp +++ b/xo-imgui/example/ex2/imgui_ex2.cpp @@ -10,6 +10,7 @@ #include "xo/randomgen/xoshiro256.hpp" #include "xo/randomgen/random_seed.hpp" #include "xo/object/Integer.hpp" +#include "xo/flatstring/flatstring.hpp" #include "xo/indentlog/scope.hpp" #include @@ -37,6 +38,10 @@ struct ImRect { ImRect() = default; ImRect(const ImVec2 & tl, const ImVec2 & br) : top_left_{tl}, bottom_right_{br} {} + static ImRect from_xy_span(const ImVec2 & x_span, const ImVec2 & y_span) { + return ImRect(ImVec2{x_span.x, y_span.x}, ImVec2{x_span.y, y_span.y}); + } + std::pair x_span() const { return std::make_pair(top_left_.x, bottom_right_.x); } std::pair y_span() const { return std::make_pair(top_left_.y, bottom_right_.y); } @@ -315,6 +320,21 @@ draw_filled_rect_with_label(const char * text, } } +void +draw_filled_rect(const char * tooltip, + const ImRect & rect, + ImU32 fillcolor, + ImDrawList * draw_list) +{ + draw_filled_rect_with_label(nullptr, + tooltip, + rect, + fillcolor, + IM_COL32(255, 255, 255, 255), + draw_list); +} + + using xo::scope; /** @@ -582,6 +602,93 @@ using xo::gc::GcStatisticsHistoryItem; using xo::xtag; using std::size_t; +/** for history tooltip, choose which statistic to headline **/ +enum class gc_history_headline { + survive, + promote, + persist, + garbage0, + garbage1, + garbageN, + N +}; + +xo::flatstring<256> +write_gc_history_tooltip(gc_history_headline headline, + const GcStatisticsHistoryItem & stats) +{ + xo::flatstring<256> retval; + + xo::flatstring<256> headline_str; + switch (headline) { + case gc_history_headline::survive: + snprintf(headline_str.data(), headline_str.capacity(), + "survive: %lu: bytes surviving 1st GC after allocation", + stats.survive_z_); + break; + case gc_history_headline::promote: + snprintf(headline_str.data(), headline_str.capacity(), + "promote: %lu: bytes surviving 2nd GC; if nursery promote to tenured", + stats.promote_z_); + break; + case gc_history_headline::persist: + snprintf(headline_str.data(), headline_str.capacity(), + "persist: %lu: bytes surviving 3+ GCs. Only non-zero for full collections", + stats.persist_z_); + break; + case gc_history_headline::garbage0: + snprintf(headline_str.data(), headline_str.capacity(), + "garbage\u2080: %lu: bytes collected on 1st GC after allocation", + stats.garbage0_z_); + break; + case gc_history_headline::garbage1: + snprintf(headline_str.data(), headline_str.capacity(), + "garbage\u2081: %lu: bytes collected on 2nd GC after allocation", + stats.garbage1_z_); + break; + case gc_history_headline::garbageN: + snprintf(headline_str.data(), headline_str.capacity(), + "garbage\u2099: %lu: bytes collected on 3rd or later GC after allocation", + stats.garbageN_z_); + break; + case gc_history_headline::N: + assert(false); + break; + } + + snprintf(retval.data(), retval.capacity(), + "%s\n" + "\n" + " gcseq: %lu\n" + " type: %s\n" + " alloc: %lu\n" + " survive: %lu\n" + " promote: %lu\n" + " persist: %lu\n" + " garbage\u2080: %lu\n" /*garbage0*/ + " garbage\u2081: %lu\n" /*garbage1*/ + " garbage\u2099: %lu\n" /*garbageN*/ + " effort: %lu dt: %.1lfus\n", + headline_str.c_str(), + stats.gc_seq_, + (stats.upto_ == generation::nursery) ? "incremental" : "FULL", + stats.new_alloc_z_, + stats.survive_z_, + stats.promote_z_, + stats.persist_z_, + stats.garbage0_z_, + stats.garbage1_z_, + stats.garbageN_z_, + stats.effort_z_, + 1e-3 * stats.dt_.scale() + ); + + return retval.ensure_final_null(); +} + +/** @param gen if @ref generation::nursery, only display nursery collections. + * otherwise display both + **/ void draw_gc_history(const GcStateDescription & gcstate, generation gen, @@ -592,7 +699,7 @@ draw_gc_history(const GcStateDescription & gcstate, { scope log(XO_DEBUG(debug_flag)); - float lm = 50; + float lm = 10; float tm = 25; /* we're going to make a bar chart */ @@ -607,18 +714,24 @@ draw_gc_history(const GcStateDescription & gcstate, /* 1st loop: figure out max y scale */ for (const GcStatisticsHistoryItem & stats : gc_history) { - size_t sz = stats.survive_z_; /*survive 1st gc */ - size_t pz = stats.promote_z_; /*survive 2nd gc */ - size_t psz = stats.persist_z_; /*survive 3+ gc */ - size_t g0z = stats.garbage0_z_; - size_t g1z = stats.garbage1_z_; - size_t gNz = stats.garbageN_z_; + if ((gen == stats.upto_) || (gen == generation::tenured)) + { + //size_t na = stats.new_alloc_z_ - stats.survive_z_; /*new allocs, but dont' double-count survive_z*/ + size_t sz = stats.survive_z_; /*survive 1st gc */ + size_t pz = stats.promote_z_; /*survive 2nd gc */ + size_t psz = stats.persist_z_; /*survive 3+ gc */ + size_t g0z = stats.garbage0_z_; + size_t g1z = stats.garbage1_z_; + size_t gNz = stats.garbageN_z_; - if (yplus_scale < sz + pz + psz) - yplus_scale = sz + pz + psz; + if (yplus_scale < sz + pz + psz) + yplus_scale = sz + pz + psz; - if (yminus_scale < g0z + g1z + gNz) - yminus_scale = g0z + g1z + gNz; + if (yminus_scale < g0z + g1z + gNz) + yminus_scale = g0z + g1z + gNz; + } else { + ; + } } /* y-coord of x-axis */ @@ -631,58 +744,124 @@ draw_gc_history(const GcStateDescription & gcstate, /* 2nd loop: draw bars */ std::size_t i = 0; - for (const GcStatisticsHistoryItem & stats : gc_history) { - /* x-coordinates of bar */ - float x_lo = lm + i * bar_w; - float x_hi = x_lo + bar_w - 1; + for (const GcStatisticsHistoryItem & stats : gc_history) + { + if ((gen == stats.upto_) || (gen == generation::tenured)) + { + /* + * ys_lo +--+ + * | | survive_z (survived 1st GC) + * | | + * yp_lo +--+ + * | | promote_z (sruvived 2nd GC) + * | | + * ypsz_lo +--+ + * | | persist_z (survived 3+ GCs) + * | | + * y_zero +--+ + * | | gN (killed on 3+ GC) + * | | + * ygN_hi +--+ + * | | g1 (killed on 2nd GC) + * | | + * yg1_hi +--+ + * | | g0 (killed on 1st GC) + * | | + * yg0_hi +--+ + */ - /* y-coordinates of persist bar (survived 3+ GCs) */ - float ypsz_lo = (y_zero - - (display_h * stats.persist_z_ / y_scale)); + ImU32 persist_color = IM_COL32( 0, 64, 192, 255); /*darker blue*/ + ImU32 promote_color = IM_COL32( 0, 128, 0, 255); /*darker green*/ + ImU32 survive_color = IM_COL32( 32, 192, 32, 255); /*lighter green*/ + ImU32 garbageN_color = IM_COL32(255, 128, 64, 255); /*darker orange*/ + ImU32 garbage1_color = IM_COL32(255, 192, 128, 255); /*medium orange*/ + ImU32 garbage0_color = IM_COL32(255, 255, 192, 255); /*pale yellow*/ - draw_list->AddRectFilled(ImVec2(x_lo, ypsz_lo), ImVec2(x_hi, y_zero), - IM_COL32( 0, 64, 192, 255) /*darker blue*/); + /* x-coordinates of bar */ + float x_lo = bounding_rect.x_lo() + lm + i * bar_w; + float x_hi = x_lo + bar_w - 1; + ImVec2 x_span{x_lo, x_hi}; - /* y-coordinates of promote bar (survived 2nd GC) */ - float yp_hi = ypsz_lo; - float yp_lo = (yp_hi - - (display_h * stats.promote_z_ / y_scale)); + /* y-coordinates of persist bar (survived 3+ GCs) */ + float ypsz_lo = (y_zero + - (display_h * stats.persist_z_ / y_scale)); + { + xo::flatstring<256> tt = write_gc_history_tooltip(gc_history_headline::persist, stats); - draw_list->AddRectFilled(ImVec2(x_lo, yp_lo), ImVec2(x_hi, yp_hi), - IM_COL32( 0, 128, 0, 255) /*darker green*/); + draw_filled_rect(tt.c_str(), + ImRect::from_xy_span(x_span, ImVec2(ypsz_lo, y_zero)), + persist_color, + draw_list); + } + /* y-coordinates of promote bar (survived 2nd GC) */ + float yp_hi = ypsz_lo; + float yp_lo = (yp_hi + - (display_h * stats.promote_z_ / y_scale)); + { + xo::flatstring<256> tt = write_gc_history_tooltip(gc_history_headline::promote, stats); - /* y-coordinates of survivor bar (survived 1st GC) */ - float ys_hi = yp_lo; - float ys_lo = (ys_hi - (display_h * stats.survive_z_ / y_scale)); + draw_filled_rect(tt.c_str(), + ImRect::from_xy_span(x_span, ImVec2(yp_lo, yp_hi)), + promote_color, + draw_list); + } - draw_list->AddRectFilled(ImVec2(x_lo, ys_lo), ImVec2(x_hi, ys_hi), - IM_COL32( 32, 192, 32, 255)); + /* y-coordinates of survivor bar (survived 1st GC) */ + float ys_hi = yp_lo; + float ys_lo = (ys_hi - (display_h * stats.survive_z_ / y_scale)); + { + xo::flatstring<256> tt = write_gc_history_tooltip(gc_history_headline::survive, stats); - // ----------------------------------------------------------- + draw_filled_rect(tt.c_str(), + ImRect::from_xy_span(x_span, ImVec2(ys_lo, ys_hi)), + survive_color, + draw_list); + } - /* y-coordinates of garbageN bar (killed on 3+ GC) */ - float ygN_lo = y_zero; - float ygN_hi = (y_zero - + (display_h * stats.garbageN_z_ / y_scale)); + // ----------------------------------------------------------- - draw_list->AddRectFilled(ImVec2(x_lo, ygN_lo), ImVec2(x_hi, ygN_hi), - IM_COL32(255, 192, 32, 255)); + /* y-coordinates of garbageN bar (killed on 3+ GC) */ + float ygN_lo = y_zero; + float ygN_hi = (y_zero + + (display_h * stats.garbageN_z_ / y_scale)); + { + xo::flatstring<256> tt = write_gc_history_tooltip(gc_history_headline::garbageN, stats); - /* y-coordinates of garbage1 bar (killed on 2nd GC) */ - float yg1_lo = ygN_hi; - float yg1_hi = (yg1_lo - + (display_h * stats.garbage1_z_ / y_scale)); + draw_filled_rect(tt.c_str(), + ImRect::from_xy_span(x_span, ImVec2(ygN_lo, ygN_hi)), + garbageN_color, + draw_list); + } - draw_list->AddRectFilled(ImVec2(x_lo, y_zero), ImVec2(x_hi, yg1_hi), - IM_COL32(192, 192, 32, 255)); + /* y-coordinates of garbage1 bar (killed on 2nd GC) */ + float yg1_lo = ygN_hi; + float yg1_hi = (yg1_lo + + (display_h * stats.garbage1_z_ / y_scale)); + { + xo::flatstring<256> tt = write_gc_history_tooltip(gc_history_headline::garbage1, stats); - /* y-coordinates of garbage0 bar (killed on 1st GC) */ - float yg0_lo = yg1_hi; - float yg0_hi = (yg0_hi - + (display_h * stats.garbage0_z_ / y_scale)); + draw_filled_rect(tt.c_str(), + ImRect(ImVec2(x_lo, yg1_lo), ImVec2(x_hi, yg1_hi)), + garbage1_color, + draw_list); + } - draw_list->AddRectFilled(ImVec2(x_lo, yg0_lo), ImVec2(x_hi, yg0_hi), - IM_COL32(255, 255, 32, 255)); + /* y-coordinates of garbage0 bar (killed on 1st GC) */ + float yg0_lo = yg1_hi; + float yg0_hi = (yg0_lo + + (display_h * stats.garbage0_z_ / y_scale)); + { + xo::flatstring<256> tt = write_gc_history_tooltip(gc_history_headline::garbage0, stats); + + draw_filled_rect(tt.c_str(), + ImRect(ImVec2(x_lo, yg0_lo), ImVec2(x_hi, yg0_hi)), + garbage0_color, + draw_list); + } + } else { + /* draw nothing */ + ; + } ++i; } @@ -791,6 +970,7 @@ draw_gc_state(const AppState & app_state, nullptr, nullptr); + /* just incremental (nursery) collections */ draw_gc_history(gcstate, generation::nursery, app_state.gc_->gc_history(), @@ -799,6 +979,15 @@ draw_gc_state(const AppState & app_state, false /*debug_flag*/, draw_list); + /* both nursery + full collections */ + draw_gc_history(gcstate, + generation::tenured, + app_state.gc_->gc_history(), + ImRect(ImVec2(x0, h_y0 + 250), + ImVec2(x1, h_y0 + 500)), + false /*debug_flag*/, + draw_list); + #ifdef NOPE draw_list->AddCircleFilled(ImVec2(canvas_p0.x + 50, canvas_p0.y + 50), 30.0f, IM_COL32(255, 0, 0, 255)); @@ -1004,7 +1193,7 @@ int main(int, char **) 0x0020, 0x00ff, // basic latin + latin supplement 0x0100, 0x017f, // latin extended-A 0x0180, 0x024f, // latin extended-B - 0x2080, 0x208a, // subscript numerals + 0x2080, 0x2099, // subscript numerals + letters through n 0x25b2, 0x25b4, // arrows 0, }; @@ -1116,7 +1305,7 @@ int main(int, char **) if (ImGui::Button("Button")) ++counter; ImGui::NewLine(); // ImGui::SameLine() - /* \u2080 = N0, \u2081 = N1 */ + /* N\u2080 = N0, N\u2081 = N1 */ ImGui::Text("alloc [%lu] avail [%lu] ", gcstate.gc_allocated_, gcstate.gc_available_); @@ -1148,11 +1337,10 @@ int main(int, char **) draw_list, &draw_state.gcw_nursery_alloc_rect_); - app_state.gc_->enable_gc(); /* GC may run here, in which case control reenters via AnimateGcCopyCb; * callback will rely on loop assignments to draw_area members. */ - app_state.gc_->disable_gc(); + app_state.gc_->enable_gc_once(); ImGui::End(); }