Files
hakmem/core/box/tiny_free_gate_box.h
Moe Charm (CI) 5685c2f4c9 Implement Warm Pool Secondary Prefill Optimization (Phase B-2c Complete)
Problem: Warm pool had 0% hit rate (only 1 hit per 3976 misses) despite being
implemented, causing all cache misses to go through expensive superslab_refill
registry scans.

Root Cause Analysis:
- Warm pool was initialized once and pushed a single slab after each refill
- When that slab was exhausted, it was discarded (not pushed back)
- Next refill would push another single slab, which was immediately exhausted
- Pool would oscillate between 0 and 1 items, yielding 0% hit rate

Solution: Secondary Prefill on Cache Miss
When warm pool becomes empty, we now do multiple superslab_refills and prefill
the pool with 3 additional HOT superlslabs before attempting to carve. This
builds a working set of slabs that can sustain allocation pressure.

Implementation Details:
- Modified unified_cache_refill() cold path to detect empty pool
- Added prefill loop: when pool count == 0, load 3 extra superlslabs
- Store extra slabs in warm pool, keep 1 in TLS for immediate carving
- Track prefill events in g_warm_pool_stats[].prefilled counter

Results (1M Random Mixed 256B allocations):
- Before: C7 hits=1, misses=3976, hit_rate=0.0%
- After:  C7 hits=3929, misses=3143, hit_rate=55.6%
- Throughput: 4.055M ops/s (maintained vs 4.07M baseline)
- Stability: Consistent 55.6% hit rate at 5M allocations (4.102M ops/s)

Performance Impact:
- No regression: throughput remained stable at ~4.1M ops/s
- Registry scan avoided in 55.6% of cache misses (significant savings)
- Warm pool now functioning as intended with strong locality

Configuration:
- TINY_WARM_POOL_MAX_PER_CLASS increased from 4 to 16 to support prefill
- Prefill budget hardcoded to 3 (tunable via env var if needed later)
- All statistics always compiled, ENV-gated printing via HAKMEM_WARM_POOL_STATS=1

Next Steps:
- Monitor for further optimization opportunities (prefill budget tuning)
- Consider adaptive prefill budget based on class-specific hit rates
- Validate at larger allocation counts (10M+ pending registry size fix)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-04 23:31:54 +09:00

182 lines
6.4 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// tiny_free_gate_box.h - Box: Tiny Free Gatekeeper
//
// 役割:
// - free エントリで Tiny 向け USER ポインタを一度だけ受け取り、
// 将来の USER→BASE 変換・Bridge・Guard を集約する「門番」の箱。
// - 現時点では既存の header-based fast free
// hak_tiny_free_fast_v2(ptr)
// への薄いラッパとして実装し、挙動を変えずに Box 境界だけを確立する。
//
// Box 理論:
// - Single Responsibility:
// 「Tiny free の入口で、Tiny Fast Path に渡すかどうかを決める」だけに限定。
// - Clear Boundary:
// hak_free_at() から Tiny Fast Path への入口は tiny_free_gate_try_fast()
// に一本化し、今後 USER→BASE / Bridge / Guard をここに集約できるようにする。
// - Reversible:
// ENV での診断強化や Guard 追加をこの箱の内側に閉じ込め、
// OFF にすれば既存挙動に戻せる構造にする。
#ifndef HAKMEM_TINY_FREE_GATE_BOX_H
#define HAKMEM_TINY_FREE_GATE_BOX_H
#include "../hakmem_build_flags.h"
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include "ptr_type_box.h"
#include "ptr_conversion_box.h"
#include "tiny_ptr_bridge_box.h"
#include "../tiny_region_id.h"
// Header-based Tiny Fast Free 実装本体
#include "../tiny_free_fast_v2.inc.h"
// 将来の拡張用コンテキスト:
// - user : 元の USER ポインタ
// - base : USER→BASE 変換後の BASE ポインタ
// - bridge: Superslab / slab / meta / class の Bridge 情報
// - class_idx: meta 由来のクラス (>=0 のとき有効)
typedef struct TinyFreeGateContext {
hak_user_ptr_t user;
hak_base_ptr_t base;
TinyPtrBridgeInfo bridge;
int class_idx;
} TinyFreeGateContext;
// 診断用 Gatekeeper 拡張の ON/OFFENV: HAKMEM_TINY_FREE_GATE_DIAG
static inline int tiny_free_gate_diag_enabled(void)
{
static __thread int s_diag = -1;
if (__builtin_expect(s_diag == -1, 0)) {
#if !HAKMEM_BUILD_RELEASE
// Debug ビルドでは env が立っていれば有効化(デフォルト OFF
const char* e = getenv("HAKMEM_TINY_FREE_GATE_DIAG");
s_diag = (e && *e && *e != '0') ? 1 : 0;
#else
(void)getenv;
s_diag = 0;
#endif
}
return s_diag;
}
// 診断用: USER ポインタから Bridge + USER→BASE を一箇所で求める。
// 戻り値:
// 1: Tiny Superslab 上の有効なブロックと判定ctx に情報を格納)
// 0: Tiny 管理外または Bridge 失敗ctx は部分的/空のまま)
static inline int tiny_free_gate_classify(void* user_ptr, TinyFreeGateContext* ctx)
{
if (!ctx) return 0;
ctx->user = HAK_USER_FROM_RAW(user_ptr);
ctx->base = HAK_BASE_FROM_RAW(NULL);
ctx->bridge.ss = NULL;
ctx->bridge.meta = NULL;
ctx->bridge.slab_idx = -1;
ctx->bridge.meta_cls = 0xffu;
ctx->class_idx = -1;
if (!user_ptr) {
return 0;
}
uintptr_t addr = (uintptr_t)user_ptr;
if (addr < 4096 || addr > 0x00007fffffffffffULL) {
// 明らかにユーザ空間外とみなして Tiny では扱わない
return 0;
}
TinyPtrBridgeInfo info = tiny_ptr_bridge_classify_raw(user_ptr);
ctx->bridge = info;
if (!info.ss || !info.meta || info.slab_idx < 0) {
return 0;
}
if (info.meta_cls >= TINY_NUM_CLASSES) {
return 0;
}
ctx->class_idx = (int)info.meta_cls;
ctx->base = ptr_user_to_base(ctx->user, ctx->class_idx);
#if !HAKMEM_BUILD_RELEASE
// 任意の追加診断: Header の class と meta class の不一致を検出
int hdr_cls = tiny_region_id_read_header(user_ptr);
if (hdr_cls >= 0 && hdr_cls != ctx->class_idx) {
static _Atomic uint32_t g_gate_hdr_meta_mis = 0;
uint32_t n = atomic_fetch_add_explicit(&g_gate_hdr_meta_mis, 1, memory_order_relaxed);
if (n < 8) {
fprintf(stderr,
"[TINY_FREE_GATE_HDR_META_MISMATCH] hdr_cls=%d meta_cls=%d ptr=%p ss=%p slab=%d\n",
hdr_cls, ctx->class_idx, user_ptr,
(void*)info.ss, info.slab_idx);
fflush(stderr);
}
}
#endif
return 1;
}
// Tiny Free Gatekeeper 本体:
// - free() ラッパから Tiny Fast Path を呼ぶ唯一の入口。
// - 既存の hak_tiny_free_fast_v2(ptr) をそのまま呼び出しつつ、
// 将来の USER→BASE+Bridge+Guard を差し込むためのフックを提供する。
//
// 戻り値:
// 1: Fast Path で処理済みTLS SLL 等に push 済み)
// 0: Slow Path にフォールバックすべきhak_tiny_free へ)
static __attribute__((always_inline)) int tiny_free_gate_try_fast(void* user_ptr)
{
#if !HAKMEM_TINY_HEADER_CLASSIDX
(void)user_ptr;
// Header 無効構成では Tiny Fast Path 自体を使わない
return 0;
#else
if (__builtin_expect(!user_ptr, 0)) {
return 0;
}
// Layer 3a: 軽量 Fail-Fast常時ON
// 明らかに不正なアドレス(極端に小さい値)は Fast Path では扱わない。
// Slow Path 側hak_free_at + registry/headerに任せる。
{
uintptr_t addr = (uintptr_t)user_ptr;
if (__builtin_expect(addr < 4096, 0)) {
#if !HAKMEM_BUILD_RELEASE
static _Atomic uint32_t g_free_gate_range_invalid = 0;
uint32_t n = atomic_fetch_add_explicit(&g_free_gate_range_invalid, 1, memory_order_relaxed);
if (n < 8) {
fprintf(stderr,
"[TINY_FREE_GATE_RANGE_INVALID] ptr=%p\n",
user_ptr);
fflush(stderr);
}
#endif
return 0;
}
}
// 将来の拡張ポイント:
// - DIAG ON のときだけ Bridge + Guard を実行し、
// Tiny 管理外と判定された場合は Fast Path をスキップする。
#if !HAKMEM_BUILD_RELEASE
if (__builtin_expect(tiny_free_gate_diag_enabled(), 0)) {
TinyFreeGateContext ctx;
if (!tiny_free_gate_classify(user_ptr, &ctx)) {
// Tiny 管理外 or Bridge 失敗 → Fast Path は使わない
return 0;
}
(void)ctx; // 現時点ではログ専用。将来はここから Guard を挿入。
}
#endif
// 本体は既存の ultra-fast free に丸投げ(挙動を変えない)
return hak_tiny_free_fast_v2(user_ptr);
#endif
}
#endif // HAKMEM_TINY_FREE_GATE_BOX_H