135 lines
4.5 KiB
C
135 lines
4.5 KiB
C
|
|
// 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);
|
|||
|
|
}
|