// tiny_stats_box.h - Cold Tiny Stats Box // 役割: // - TinyHeap/TinyFront のホットパスから meta->used / ss_active_* 更新を切り出す箱。 // - C7 SAFE の delta flush を「即時適用 or バッチ適用」に切り替える足場。 #pragma once #include #include #include // 前方宣言(利用側で tiny_heap_page_t を定義済みであることが前提) typedef struct tiny_heap_page_t tiny_heap_page_t; typedef struct SuperSlab SuperSlab; // ENV: HAKMEM_TINY_STATS_BOX=1 で Stats Box 経由の更新を有効化 static inline int tiny_stats_box_enabled(void) { static int g = -1; if (__builtin_expect(g == -1, 0)) { const char* e = getenv("HAKMEM_TINY_STATS_BOX"); g = (e && *e && *e != '0') ? 1 : 0; } return g; } // ENV: HAKMEM_TINY_STATS_BATCH=1 で meta/active をバッチ適用(pending に貯める) static inline int tiny_stats_batch_enabled(void) { static int g = -1; if (__builtin_expect(g == -1, 0)) { const char* e = getenv("HAKMEM_TINY_STATS_BATCH"); g = (e && *e && *e != '0') ? 1 : 0; } return g; } typedef struct { uint32_t alloc_delta; uint32_t free_delta; } tiny_stats_accum_t; // C7 SAFE の pending を扱うための薄い構造体(page 内に埋め込まれる想定) typedef struct { int32_t used_delta; int32_t active_delta; } tiny_stats_pending_t; // Hot からのイベントを受け取るフック(現状は no-op) static inline void tiny_stats_on_alloc(int class_idx, tiny_heap_page_t* page) { (void)class_idx; (void)page; if (!tiny_stats_box_enabled()) return; // TODO: ここにクラス別の軽量カウンタを積む余地を残す } static inline void tiny_stats_on_free(int class_idx, tiny_heap_page_t* page) { (void)class_idx; (void)page; if (!tiny_stats_box_enabled()) return; // TODO: ここにクラス別の軽量カウンタを積む余地を残す } // Superslab active counters(既存 Box の関数をここから呼ぶ) void ss_active_add(SuperSlab* ss, uint32_t n); void ss_active_dec_one(SuperSlab* ss); static inline void tiny_stats_apply_to_meta(tiny_heap_page_t* page, int32_t used_delta, int32_t active_delta) { if (!page || !page->meta || !page->ss) return; if (used_delta != 0) { atomic_fetch_add_explicit(&page->meta->used, used_delta, memory_order_relaxed); } if (active_delta != 0) { if (active_delta > 0) { ss_active_add(page->ss, (uint32_t)active_delta); } else { int32_t n = -active_delta; for (int32_t i = 0; i < n; i++) { ss_active_dec_one(page->ss); } } } } // バッチの flush 判定(force=1 で無条件 flush) static inline void tiny_stats_maybe_flush_for_page(int class_idx, tiny_heap_page_t* page, int force) { if (!tiny_stats_box_enabled()) return; if (!tiny_stats_batch_enabled()) return; if (!page) return; (void)class_idx; int32_t ud = page->stats_used_pending; int32_t ad = page->stats_active_pending; int32_t abs_ud = (ud >= 0) ? ud : -ud; int32_t abs_ad = (ad >= 0) ? ad : -ad; int32_t abs_max = (abs_ud > abs_ad) ? abs_ud : abs_ad; uint16_t cap = page->capacity; int32_t th = (cap > 0) ? ((int32_t)cap * 16) : 256; if (th < 256) th = 256; if (!force && abs_max < th) { return; } if (ud != 0 || ad != 0) { tiny_stats_apply_to_meta(page, ud, ad); page->stats_used_pending = 0; page->stats_active_pending = 0; } } // flush: delta を Cold Stats 側に渡す(即時 or pending) static inline void tiny_stats_flush_for_page(int class_idx, tiny_heap_page_t* page, int32_t used_delta, int32_t active_delta) { if (!page) return; if (!tiny_stats_box_enabled()) { tiny_stats_apply_to_meta(page, used_delta, active_delta); return; } if (!page->meta || !page->ss) return; if (used_delta == 0 && active_delta == 0) { // すでに pending があれば empty 時に flush される if (tiny_stats_batch_enabled()) { tiny_stats_maybe_flush_for_page(class_idx, page, page->used == 0); } return; } if (!tiny_stats_batch_enabled()) { tiny_stats_apply_to_meta(page, used_delta, active_delta); return; } page->stats_used_pending += used_delta; page->stats_active_pending += active_delta; tiny_stats_maybe_flush_for_page(class_idx, page, page->used == 0); }