Files
hakmem/core/box/tiny_stats_box.h

135 lines
4.5 KiB
C
Raw Normal View History

// tiny_stats_box.h - Cold Tiny Stats Box
// 役割:
// - TinyHeap/TinyFront のホットパスから meta->used / ss_active_* 更新を切り出す箱。
// - C7 SAFE の delta flush を「即時適用 or バッチ適用」に切り替える足場。
#pragma once
#include <stdint.h>
#include <stdlib.h>
#include <stdatomic.h>
// 前方宣言(利用側で 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);
}