Files
hakmem/core/hakmem_shared_pool_acquire.c
Moe Charm (CI) a6ab262ad2 Phase 70-0: Infrastructure prep for refill tuning (Observability + WarmPool Sizing)
- Observability Fix: Enabled unified cache hit/miss stats in release builds when HAKMEM_UNIFIED_CACHE_STATS_COMPILED is set.
- WarmPool Sizing: Decoupled hardcoded '12' from prefill logic; now uses TINY_WARM_POOL_DEFAULT_PER_CLASS macro and respects ENV overrides.
- Increased TINY_WARM_POOL_MAX_PER_CLASS to 32 to support wider ENV sweeps.
- Added unified_cache_auto_stats destructor to dump metrics at exit (replacing debug print hack).
2025-12-18 03:02:40 +09:00

1044 lines
43 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.

#include "hakmem_shared_pool_internal.h"
#include "hakmem_debug_master.h"
#include "hakmem_stats_master.h"
#include "box/ss_slab_meta_box.h"
#include "box/ss_hot_cold_box.h"
#include "box/pagefault_telemetry_box.h"
#include "box/tls_sll_drain_box.h"
#include "box/tls_slab_reuse_guard_box.h"
#include "box/ss_tier_box.h" // P-Tier: Tier filtering support
#include "hakmem_policy.h"
#include "hakmem_env_cache.h" // Priority-2: ENV cache
#include "front/tiny_warm_pool.h" // Warm Pool: Prefill during registry scans
#include "box/ss_slab_reset_box.h" // Box: Reset slab metadata on reuse (C7 guard)
#include "box/tiny_class_stats_box.h" // OBSERVE: per-class shared lock stats
#include "box/ss_stats_box.h" // OBSERVE: Superslab/slab event counters
#include "box/ss_budget_box.h" // Budget guard for Superslab growth (larson_guard)
#include "box/super_reg_box.h" // Logical limit for registry scan
#include "box/shared_pool_box.h" // Logical cap for shared pool slots (bench profile)
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdatomic.h>
// Stage3(LRU) 由来の Superslab をトレースするための簡易マジック
_Atomic uintptr_t g_c7_stage3_magic_ss = 0;
static inline void sp_lock_with_stats(int class_idx) {
pthread_mutex_lock(&g_shared_pool.alloc_lock);
tiny_class_stats_on_shared_lock(class_idx);
}
static inline void c7_log_meta_state(const char* tag, SuperSlab* ss, int slab_idx) {
if (!ss) return;
#if HAKMEM_BUILD_RELEASE
static _Atomic uint32_t rel_c7_meta_logs = 0;
uint32_t n = atomic_fetch_add_explicit(&rel_c7_meta_logs, 1, memory_order_relaxed);
if (n < 8) {
TinySlabMeta* m = &ss->slabs[slab_idx];
fprintf(stderr,
"[REL_C7_%s] ss=%p slab=%d cls=%u used=%u cap=%u carved=%u freelist=%p\n",
tag,
(void*)ss,
slab_idx,
(unsigned)m->class_idx,
(unsigned)m->used,
(unsigned)m->capacity,
(unsigned)m->carved,
m->freelist);
}
#else
static _Atomic uint32_t dbg_c7_meta_logs = 0;
uint32_t n = atomic_fetch_add_explicit(&dbg_c7_meta_logs, 1, memory_order_relaxed);
if (n < 8) {
TinySlabMeta* m = &ss->slabs[slab_idx];
fprintf(stderr,
"[DBG_C7_%s] ss=%p slab=%d cls=%u used=%u cap=%u carved=%u freelist=%p\n",
tag,
(void*)ss,
slab_idx,
(unsigned)m->class_idx,
(unsigned)m->used,
(unsigned)m->capacity,
(unsigned)m->carved,
m->freelist);
}
#endif
}
static inline int c7_meta_is_pristine(TinySlabMeta* m) {
return m && m->used == 0 && m->carved == 0 && m->freelist == NULL;
}
static inline void c7_log_skip_nonempty_acquire(SuperSlab* ss,
int slab_idx,
TinySlabMeta* m,
const char* tag) {
if (!(ss && m)) return;
#if HAKMEM_BUILD_RELEASE
static _Atomic uint32_t rel_c7_skip_logs = 0;
uint32_t n = atomic_fetch_add_explicit(&rel_c7_skip_logs, 1, memory_order_relaxed);
if (n < 4) {
fprintf(stderr,
"[REL_C7_%s] ss=%p slab=%d cls=%u used=%u cap=%u carved=%u freelist=%p\n",
tag,
(void*)ss,
slab_idx,
(unsigned)m->class_idx,
(unsigned)m->used,
(unsigned)m->capacity,
(unsigned)m->carved,
m->freelist);
}
#else
static _Atomic uint32_t dbg_c7_skip_logs = 0;
uint32_t n = atomic_fetch_add_explicit(&dbg_c7_skip_logs, 1, memory_order_relaxed);
if (n < 4) {
fprintf(stderr,
"[DBG_C7_%s] ss=%p slab=%d cls=%u used=%u cap=%u carved=%u freelist=%p\n",
tag,
(void*)ss,
slab_idx,
(unsigned)m->class_idx,
(unsigned)m->used,
(unsigned)m->capacity,
(unsigned)m->carved,
m->freelist);
}
#endif
}
static inline int c7_reset_and_log_if_needed(SuperSlab* ss,
int slab_idx,
int class_idx) {
if (class_idx != 7) {
return 0;
}
TinySlabMeta* m = &ss->slabs[slab_idx];
c7_log_meta_state("ACQUIRE_META", ss, slab_idx);
if (m->class_idx != 255 && m->class_idx != (uint8_t)class_idx) {
#if HAKMEM_BUILD_RELEASE
static _Atomic uint32_t rel_c7_class_mismatch_logs = 0;
uint32_t n = atomic_fetch_add_explicit(&rel_c7_class_mismatch_logs, 1, memory_order_relaxed);
if (n < 4) {
fprintf(stderr,
"[REL_C7_CLASS_MISMATCH] ss=%p slab=%d want=%d have=%u used=%u cap=%u carved=%u\n",
(void*)ss,
slab_idx,
class_idx,
(unsigned)m->class_idx,
(unsigned)m->used,
(unsigned)m->capacity,
(unsigned)m->carved);
}
#else
static _Atomic uint32_t dbg_c7_class_mismatch_logs = 0;
uint32_t n = atomic_fetch_add_explicit(&dbg_c7_class_mismatch_logs, 1, memory_order_relaxed);
if (n < 4) {
fprintf(stderr,
"[DBG_C7_CLASS_MISMATCH] ss=%p slab=%d want=%d have=%u used=%u cap=%u carved=%u freelist=%p\n",
(void*)ss,
slab_idx,
class_idx,
(unsigned)m->class_idx,
(unsigned)m->used,
(unsigned)m->capacity,
(unsigned)m->carved,
m->freelist);
}
#endif
return -1;
}
if (!c7_meta_is_pristine(m)) {
c7_log_skip_nonempty_acquire(ss, slab_idx, m, "SKIP_NONEMPTY_ACQUIRE");
return -1;
}
ss_slab_reset_meta_for_tiny(ss, slab_idx, class_idx);
c7_log_meta_state("ACQUIRE", ss, slab_idx);
return 0;
}
static inline void sp_reset_superslab_all_slabs(SuperSlab* ss,
int class_idx,
int from_lru) {
if (!ss) {
return;
}
int cap = ss_slabs_capacity(ss);
ss->slab_bitmap = 0;
ss->nonempty_mask = 0;
ss->freelist_mask = 0;
ss->empty_mask = 0;
ss->empty_count = 0;
ss->active_slabs = 0;
ss->hot_count = 0;
ss->cold_count = 0;
for (int s = 0; s < cap; s++) {
ss_slab_reset_meta_for_tiny(ss, s, class_idx);
}
ss_stats_on_ss_scan(class_idx, 0, 1);
static _Atomic uint32_t rel_stage3_reset_logs = 0;
uint32_t n = atomic_fetch_add_explicit(&rel_stage3_reset_logs, 1, memory_order_relaxed);
if (n < 4) {
fprintf(stderr,
"[REL_STAGE3_RESET] class=%d ss=%p from_lru=%d cap=%d\n",
class_idx,
(void*)ss,
from_lru,
cap);
}
}
// ============================================================================
// Performance Measurement: Shared Pool Lock Contention (ENV-gated)
// ============================================================================
// Global atomic counters for lock contention measurement
// ENV: HAKMEM_MEASURE_UNIFIED_CACHE=1 to enable (default: OFF)
_Atomic uint64_t g_sp_stage2_lock_acquired_global = 0;
_Atomic uint64_t g_sp_stage3_lock_acquired_global = 0;
_Atomic uint64_t g_sp_alloc_lock_contention_global = 0;
// Per-class lock acquisition statisticsTiny クラス別の lock 負荷観測用)
_Atomic uint64_t g_sp_stage2_lock_acquired_by_class[TINY_NUM_CLASSES_SS] = {0};
_Atomic uint64_t g_sp_stage3_lock_acquired_by_class[TINY_NUM_CLASSES_SS] = {0};
// Check if measurement is enabled (cached)
static inline int sp_measure_enabled(void) {
static int g_measure = -1;
if (__builtin_expect(g_measure == -1, 0)) {
const char* e = getenv("HAKMEM_MEASURE_UNIFIED_CACHE");
g_measure = (e && *e && *e != '0') ? 1 : 0;
if (g_measure == 1) {
// Measurement が ON のときは per-class stage stats も有効化する
// Stage1/2/3 ヒット数は g_sp_stage*_hits に集計される)
extern int g_sp_stage_stats_enabled;
g_sp_stage_stats_enabled = 1;
}
}
return g_measure;
}
// Print statistics function
void shared_pool_print_measurements(void);
// Stage 0.5: EMPTY slab direct scanregistry ベースの EMPTY 再利用)
// Scan existing SuperSlabs for EMPTY slabs (highest reuse priority) to
// avoid Stage 3 (mmap) when freed slabs are available.
//
// WARM POOL OPTIMIZATION:
// - During the registry scan, prefill warm pool with HOT SuperSlabs
// - This eliminates future registry scans for cache misses
// - Expected gain: +40-50% by reducing O(N) scan overhead
static inline int
sp_acquire_from_empty_scan(int class_idx, SuperSlab** ss_out, int* slab_idx_out, int dbg_acquire)
{
// Priority-2: Use cached ENV
int empty_reuse_enabled = HAK_ENV_SS_EMPTY_REUSE();
if (!empty_reuse_enabled) {
return -1;
}
extern int g_super_reg_class_size[TINY_NUM_CLASSES];
int reg_size = (class_idx < TINY_NUM_CLASSES) ? g_super_reg_class_size[class_idx] : 0;
int reg_cap = super_reg_effective_per_class();
if (reg_cap > 0 && reg_size > reg_cap) {
reg_size = reg_cap;
}
// Priority-2: Use cached ENV
int scan_limit = HAK_ENV_SS_EMPTY_SCAN_LIMIT();
if (scan_limit > reg_size) scan_limit = reg_size;
// Stage 0.5 hit counter for visualization
static _Atomic uint64_t stage05_hits = 0;
static _Atomic uint64_t stage05_attempts = 0;
atomic_fetch_add_explicit(&stage05_attempts, 1, memory_order_relaxed);
// Initialize warm pool on first use (per-thread, one-time)
tiny_warm_pool_init_once();
// Track SuperSlabs scanned during this acquire call for warm pool prefill
SuperSlab* primary_result = NULL;
int primary_slab_idx = -1;
// Cache warm pool cap once per acquire call (SSOT: same as unified_cache_refill()).
const int warm_cap = warm_pool_max_per_class();
for (int i = 0; i < scan_limit; i++) {
SuperSlab* ss = super_reg_by_class_at(class_idx, i);
if (!(ss && ss->magic == SUPERSLAB_MAGIC)) continue;
// P-Tier: Skip DRAINING tier SuperSlabs
if (!ss_tier_is_hot(ss)) continue;
if (ss->empty_count == 0) continue; // No EMPTY slabs in this SS
// WARM POOL PREFILL: Add HOT SuperSlabs to warm pool (if not already primary result)
// This is low-cost during registry scan and avoids future expensive scans.
if (ss != primary_result && tiny_warm_pool_count(class_idx) < warm_cap) {
tiny_warm_pool_push(class_idx, ss);
// Track prefilled SuperSlabs for metrics
g_warm_pool_stats[class_idx].prefilled++;
}
uint32_t mask = ss->empty_mask;
while (mask) {
int empty_idx = __builtin_ctz(mask);
mask &= (mask - 1); // clear lowest bit
TinySlabMeta* meta = &ss->slabs[empty_idx];
if (meta->capacity > 0 && meta->used == 0) {
tiny_tls_slab_reuse_guard(ss);
ss_clear_slab_empty(ss, empty_idx);
meta->class_idx = (uint8_t)class_idx;
ss->class_map[empty_idx] = (uint8_t)class_idx;
#if !HAKMEM_BUILD_RELEASE
if (dbg_acquire == 1) {
fprintf(stderr,
"[SP_ACQUIRE_STAGE0.5_EMPTY] class=%d reusing EMPTY slab (ss=%p slab=%d empty_count=%u warm_pool_size=%d)\n",
class_idx, (void*)ss, empty_idx, ss->empty_count, tiny_warm_pool_count(class_idx));
}
#else
(void)dbg_acquire;
#endif
// Store primary result but continue scanning to prefill warm pool
if (primary_result == NULL) {
primary_result = ss;
primary_slab_idx = empty_idx;
*ss_out = ss;
*slab_idx_out = empty_idx;
sp_stage_stats_init();
if (g_sp_stage_stats_enabled) {
atomic_fetch_add(&g_sp_stage1_hits[class_idx], 1);
}
atomic_fetch_add_explicit(&stage05_hits, 1, memory_order_relaxed);
}
}
}
}
if (primary_result != NULL) {
// Stage 0.5 hit rate visualization (every 100 hits)
uint64_t hits = atomic_load_explicit(&stage05_hits, memory_order_relaxed);
if (hits % 100 == 1) {
uint64_t attempts = atomic_load_explicit(&stage05_attempts, memory_order_relaxed);
fprintf(stderr, "[STAGE0.5_STATS] hits=%lu attempts=%lu rate=%.1f%% (scan_limit=%d warm_pool=%d)\n",
hits, attempts, (double)hits * 100.0 / attempts, scan_limit, tiny_warm_pool_count(class_idx));
}
if (c7_reset_and_log_if_needed(primary_result, primary_slab_idx, class_idx) == 0) {
return 0;
}
primary_result = NULL;
*ss_out = NULL;
*slab_idx_out = -1;
}
return -1;
}
int
shared_pool_acquire_slab(int class_idx, SuperSlab** ss_out, int* slab_idx_out)
{
// Phase 12: SP-SLOT Box - 3-Stage Acquire Logic
//
// Stage 1: Reuse EMPTY slots from per-class free list (EMPTY→ACTIVE)
// Stage 2: Find UNUSED slots in existing SuperSlabs
// Stage 3: Get new SuperSlab (LRU pop or mmap)
//
// Invariants:
// - On success: *ss_out != NULL, 0 <= *slab_idx_out < total_slots
// - The chosen slab has meta->class_idx == class_idx
if (!ss_out || !slab_idx_out) {
return -1;
}
if (class_idx < 0 || class_idx >= TINY_NUM_CLASSES_SS) {
return -1;
}
shared_pool_init();
// Debug logging / stage stats
#if !HAKMEM_BUILD_RELEASE
// Priority-2: Use cached ENV
int dbg_acquire = HAK_ENV_SS_ACQUIRE_DEBUG();
#else
static const int dbg_acquire = 0;
#endif
sp_stage_stats_init();
stage1_retry_after_tension_drain:
// ========== Stage 0.5 (Phase 12-1.1): EMPTY slab direct scan ==========
// Scan existing SuperSlabs for EMPTY slabs (highest reuse priority) to
// avoid Stage 3 (mmap) when freed slabs are available.
if (sp_acquire_from_empty_scan(class_idx, ss_out, slab_idx_out, dbg_acquire) == 0) {
return 0;
}
// ========== Stage 1 (Lock-Free): Try to reuse EMPTY slots ==========
// P0-4: Lock-free pop from per-class free list (no mutex needed!)
// Best case: Same class freed a slot, reuse immediately (cache-hot)
SharedSSMeta* reuse_meta = NULL;
int reuse_slot_idx = -1;
if (sp_freelist_pop_lockfree(class_idx, &reuse_meta, &reuse_slot_idx)) {
// Found EMPTY slot from lock-free list!
// Now acquire mutex ONLY for slot activation and metadata update
// P0 instrumentation: count lock acquisitions
lock_stats_init();
if (g_lock_stats_enabled == 1) {
atomic_fetch_add(&g_lock_acquire_count, 1);
atomic_fetch_add(&g_lock_acquire_slab_count, 1);
}
sp_lock_with_stats(class_idx);
// P0.3: Guard against TLS SLL orphaned pointers before reusing slab
// RACE FIX: Load SuperSlab pointer atomically BEFORE guard (consistency)
SuperSlab* ss_guard = atomic_load_explicit(&reuse_meta->ss, memory_order_relaxed);
if (ss_guard) {
tiny_tls_slab_reuse_guard(ss_guard);
if (class_idx == 7) {
TinySlabMeta* meta = &ss_guard->slabs[reuse_slot_idx];
int meta_ok = (meta->used == 0) && (meta->carved == 0) && (meta->freelist == NULL);
if (!meta_ok) {
c7_log_skip_nonempty_acquire(ss_guard, reuse_slot_idx, meta, "SKIP_NONEMPTY_ACQUIRE");
sp_freelist_push_lockfree(class_idx, reuse_meta, reuse_slot_idx);
goto stage2_fallback;
}
}
// P-Tier: Skip DRAINING tier SuperSlabs
if (!ss_tier_is_hot(ss_guard)) {
// DRAINING SuperSlab - skip this slot and fall through to Stage 2
if (g_lock_stats_enabled == 1) {
atomic_fetch_add(&g_lock_release_count, 1);
}
pthread_mutex_unlock(&g_shared_pool.alloc_lock);
goto stage2_fallback;
}
}
// Activate slot under mutex (slot state transition requires protection)
if (sp_slot_mark_active(reuse_meta, reuse_slot_idx, class_idx) == 0) {
// RACE FIX: Load SuperSlab pointer atomically (consistency)
SuperSlab* ss = atomic_load_explicit(&reuse_meta->ss, memory_order_relaxed);
// RACE FIX: Check if SuperSlab was freed (NULL pointer)
// This can happen if Thread A freed the SuperSlab after pushing slot to freelist,
// but Thread B popped the stale slot before the freelist was cleared.
if (!ss) {
// SuperSlab freed - skip and fall through to Stage 2/3
if (g_lock_stats_enabled == 1) {
atomic_fetch_add(&g_lock_release_count, 1);
}
pthread_mutex_unlock(&g_shared_pool.alloc_lock);
goto stage2_fallback;
}
#if !HAKMEM_BUILD_RELEASE
if (dbg_acquire == 1) {
fprintf(stderr, "[SP_ACQUIRE_STAGE1_LOCKFREE] class=%d reusing EMPTY slot (ss=%p slab=%d)\n",
class_idx, (void*)ss, reuse_slot_idx);
}
#endif
// Update SuperSlab metadata
ss->slab_bitmap |= (1u << reuse_slot_idx);
ss_slab_meta_class_idx_set(ss, reuse_slot_idx, (uint8_t)class_idx);
if (ss->active_slabs == 0) {
// Was empty, now active again
ss->active_slabs = 1;
g_shared_pool.active_count++;
}
// Track per-class active slots (approximate, under alloc_lock)
if (class_idx < TINY_NUM_CLASSES_SS) {
g_shared_pool.class_active_slots[class_idx]++;
}
// Update hint
g_shared_pool.class_hints[class_idx] = ss;
*ss_out = ss;
*slab_idx_out = reuse_slot_idx;
if (class_idx == 7) {
TinySlabMeta* meta_check = &ss->slabs[reuse_slot_idx];
if (!((meta_check->used == 0) && (meta_check->carved == 0) && (meta_check->freelist == NULL))) {
sp_freelist_push_lockfree(class_idx, reuse_meta, reuse_slot_idx);
if (g_lock_stats_enabled == 1) {
atomic_fetch_add(&g_lock_release_count, 1);
}
pthread_mutex_unlock(&g_shared_pool.alloc_lock);
goto stage2_fallback;
}
}
if (c7_reset_and_log_if_needed(ss, reuse_slot_idx, class_idx) != 0) {
*ss_out = NULL;
*slab_idx_out = -1;
if (g_lock_stats_enabled == 1) {
atomic_fetch_add(&g_lock_release_count, 1);
}
pthread_mutex_unlock(&g_shared_pool.alloc_lock);
goto stage2_fallback;
}
if (g_lock_stats_enabled == 1) {
atomic_fetch_add(&g_lock_release_count, 1);
}
pthread_mutex_unlock(&g_shared_pool.alloc_lock);
if (g_sp_stage_stats_enabled) {
atomic_fetch_add(&g_sp_stage1_hits[class_idx], 1);
}
return 0; // ✅ Stage 1 (lock-free) success
}
// Slot activation failed (race condition?) - release lock and fall through
if (g_lock_stats_enabled == 1) {
atomic_fetch_add(&g_lock_release_count, 1);
}
pthread_mutex_unlock(&g_shared_pool.alloc_lock);
}
stage2_fallback:
// ========== Stage 2 (Lock-Free): Try to claim UNUSED slots ==========
// P0 Optimization: Try class hint FIRST for fast path (same class locality)
// This reduces metadata scan from 100% to ~10% when hints are effective
{
SuperSlab* hint_ss = g_shared_pool.class_hints[class_idx];
if (__builtin_expect(hint_ss != NULL, 1)) {
// P-Tier: Skip DRAINING tier SuperSlabs
if (!ss_tier_is_hot(hint_ss)) {
// Clear stale hint pointing to DRAINING SuperSlab
g_shared_pool.class_hints[class_idx] = NULL;
goto stage2_scan;
}
// P0 Optimization: O(1) lookup via cached pointer (avoids metadata scan)
SharedSSMeta* hint_meta = hint_ss->shared_meta;
if (__builtin_expect(hint_meta != NULL, 1)) {
// Try lock-free claiming on hint SuperSlab first
int claimed_idx = sp_slot_claim_lockfree(hint_meta, class_idx);
if (__builtin_expect(claimed_idx >= 0, 1)) {
// Fast path success! No need to scan all metadata
SuperSlab* ss = atomic_load_explicit(&hint_meta->ss, memory_order_acquire);
if (__builtin_expect(ss != NULL, 1)) {
#if !HAKMEM_BUILD_RELEASE
if (dbg_acquire == 1) {
fprintf(stderr, "[SP_ACQUIRE_STAGE2_HINT] class=%d claimed UNUSED slot from hint (ss=%p slab=%d)\n",
class_idx, (void*)ss, claimed_idx);
}
#endif
// P0 instrumentation: count lock acquisitions
lock_stats_init();
if (g_lock_stats_enabled == 1) {
atomic_fetch_add(&g_lock_acquire_count, 1);
atomic_fetch_add(&g_lock_acquire_slab_count, 1);
}
sp_lock_with_stats(class_idx);
// Performance measurement: count Stage 2 lock acquisitions
if (__builtin_expect(sp_measure_enabled(), 0)) {
atomic_fetch_add_explicit(&g_sp_stage2_lock_acquired_global,
1, memory_order_relaxed);
atomic_fetch_add_explicit(&g_sp_alloc_lock_contention_global,
1, memory_order_relaxed);
atomic_fetch_add_explicit(
&g_sp_stage2_lock_acquired_by_class[class_idx],
1, memory_order_relaxed);
}
if (class_idx == 7) {
TinySlabMeta* meta = &ss->slabs[claimed_idx];
int meta_ok = (meta->used == 0) && (meta->carved == 0) &&
(meta->freelist == NULL);
if (!meta_ok) {
c7_log_skip_nonempty_acquire(ss, claimed_idx, meta, "SKIP_NONEMPTY_ACQUIRE");
sp_slot_mark_empty(hint_meta, claimed_idx);
if (g_lock_stats_enabled == 1) {
atomic_fetch_add(&g_lock_release_count, 1);
}
pthread_mutex_unlock(&g_shared_pool.alloc_lock);
goto stage2_scan;
}
}
// Update SuperSlab metadata under mutex
ss->slab_bitmap |= (1u << claimed_idx);
ss_slab_meta_class_idx_set(ss, claimed_idx, (uint8_t)class_idx);
if (ss->active_slabs == 0) {
ss->active_slabs = 1;
g_shared_pool.active_count++;
}
if (class_idx < TINY_NUM_CLASSES_SS) {
g_shared_pool.class_active_slots[class_idx]++;
}
// Hint is still good, no need to update
*ss_out = ss;
*slab_idx_out = claimed_idx;
if (class_idx == 7) {
TinySlabMeta* meta_check = &ss->slabs[claimed_idx];
if (!((meta_check->used == 0) && (meta_check->carved == 0) &&
(meta_check->freelist == NULL))) {
sp_slot_mark_empty(hint_meta, claimed_idx);
*ss_out = NULL;
*slab_idx_out = -1;
if (g_lock_stats_enabled == 1) {
atomic_fetch_add(&g_lock_release_count, 1);
}
pthread_mutex_unlock(&g_shared_pool.alloc_lock);
goto stage2_scan;
}
}
if (c7_reset_and_log_if_needed(ss, claimed_idx, class_idx) != 0) {
*ss_out = NULL;
*slab_idx_out = -1;
if (g_lock_stats_enabled == 1) {
atomic_fetch_add(&g_lock_release_count, 1);
}
pthread_mutex_unlock(&g_shared_pool.alloc_lock);
goto stage2_scan;
}
sp_fix_geometry_if_needed(ss, claimed_idx, class_idx);
if (g_lock_stats_enabled == 1) {
atomic_fetch_add(&g_lock_release_count, 1);
}
pthread_mutex_unlock(&g_shared_pool.alloc_lock);
if (g_sp_stage_stats_enabled) {
atomic_fetch_add(&g_sp_stage2_hits[class_idx], 1);
}
return 0; // ✅ Stage 2 (hint fast path) success
}
}
}
}
}
stage2_scan:
// P0-5: Lock-free atomic CAS claiming (no mutex needed for slot state transition!)
// RACE FIX: Read ss_meta_count atomically (now properly declared as _Atomic)
// No cast needed! memory_order_acquire synchronizes with release in sp_meta_find_or_create
uint32_t meta_count = atomic_load_explicit(
&g_shared_pool.ss_meta_count,
memory_order_acquire
);
for (uint32_t i = 0; i < meta_count; i++) {
SharedSSMeta* meta = &g_shared_pool.ss_metadata[i];
// RACE FIX: Load SuperSlab pointer atomically BEFORE claiming
// Use memory_order_acquire to synchronize with release in sp_meta_find_or_create
SuperSlab* ss_preflight = atomic_load_explicit(&meta->ss, memory_order_acquire);
if (!ss_preflight) {
// SuperSlab was freed - skip this entry
continue;
}
// P-Tier: Skip DRAINING tier SuperSlabs
if (!ss_tier_is_hot(ss_preflight)) {
continue;
}
// Try lock-free claiming (UNUSED → ACTIVE via CAS)
int claimed_idx = sp_slot_claim_lockfree(meta, class_idx);
if (claimed_idx >= 0) {
// RACE FIX: Load SuperSlab pointer atomically again after claiming
// Use memory_order_acquire to synchronize with release in sp_meta_find_or_create
SuperSlab* ss = atomic_load_explicit(&meta->ss, memory_order_acquire);
if (!ss) {
// SuperSlab was freed between claiming and loading - skip this entry
continue;
}
#if !HAKMEM_BUILD_RELEASE
if (dbg_acquire == 1) {
fprintf(stderr, "[SP_ACQUIRE_STAGE2_LOCKFREE] class=%d claimed UNUSED slot (ss=%p slab=%d)\n",
class_idx, (void*)ss, claimed_idx);
}
#endif
// P0 instrumentation: count lock acquisitions
lock_stats_init();
if (g_lock_stats_enabled == 1) {
atomic_fetch_add(&g_lock_acquire_count, 1);
atomic_fetch_add(&g_lock_acquire_slab_count, 1);
}
sp_lock_with_stats(class_idx);
// Performance measurement: count Stage 2 scan lock acquisitions
if (__builtin_expect(sp_measure_enabled(), 0)) {
atomic_fetch_add_explicit(&g_sp_stage2_lock_acquired_global,
1, memory_order_relaxed);
atomic_fetch_add_explicit(&g_sp_alloc_lock_contention_global,
1, memory_order_relaxed);
atomic_fetch_add_explicit(
&g_sp_stage2_lock_acquired_by_class[class_idx],
1, memory_order_relaxed);
}
if (class_idx == 7) {
TinySlabMeta* meta_slab = &ss->slabs[claimed_idx];
int meta_ok = (meta_slab->used == 0) && (meta_slab->carved == 0) &&
(meta_slab->freelist == NULL);
if (!meta_ok) {
c7_log_skip_nonempty_acquire(ss, claimed_idx, meta_slab, "SKIP_NONEMPTY_ACQUIRE");
sp_slot_mark_empty(meta, claimed_idx);
if (g_lock_stats_enabled == 1) {
atomic_fetch_add(&g_lock_release_count, 1);
}
pthread_mutex_unlock(&g_shared_pool.alloc_lock);
continue;
}
}
// Update SuperSlab metadata under mutex
ss->slab_bitmap |= (1u << claimed_idx);
ss_slab_meta_class_idx_set(ss, claimed_idx, (uint8_t)class_idx);
if (ss->active_slabs == 0) {
ss->active_slabs = 1;
g_shared_pool.active_count++;
}
if (class_idx < TINY_NUM_CLASSES_SS) {
g_shared_pool.class_active_slots[class_idx]++;
}
// Update hint
g_shared_pool.class_hints[class_idx] = ss;
*ss_out = ss;
*slab_idx_out = claimed_idx;
if (class_idx == 7) {
TinySlabMeta* meta_check = &ss->slabs[claimed_idx];
if (!((meta_check->used == 0) && (meta_check->carved == 0) &&
(meta_check->freelist == NULL))) {
sp_slot_mark_empty(meta, claimed_idx);
*ss_out = NULL;
*slab_idx_out = -1;
if (g_lock_stats_enabled == 1) {
atomic_fetch_add(&g_lock_release_count, 1);
}
pthread_mutex_unlock(&g_shared_pool.alloc_lock);
continue;
}
}
if (c7_reset_and_log_if_needed(ss, claimed_idx, class_idx) != 0) {
*ss_out = NULL;
*slab_idx_out = -1;
if (g_lock_stats_enabled == 1) {
atomic_fetch_add(&g_lock_release_count, 1);
}
pthread_mutex_unlock(&g_shared_pool.alloc_lock);
continue;
}
sp_fix_geometry_if_needed(ss, claimed_idx, class_idx);
if (g_lock_stats_enabled == 1) {
atomic_fetch_add(&g_lock_release_count, 1);
}
pthread_mutex_unlock(&g_shared_pool.alloc_lock);
if (g_sp_stage_stats_enabled) {
atomic_fetch_add(&g_sp_stage2_hits[class_idx], 1);
}
return 0; // ✅ Stage 2 (lock-free) success
}
// Claim failed (no UNUSED slots in this meta) - continue to next SuperSlab
}
// ========== Tension-Based Drain: Try to create EMPTY slots before Stage 3 ==========
// If TLS SLL has accumulated blocks, drain them to enable EMPTY slot detection
// This can avoid allocating new SuperSlabs by reusing EMPTY slots in Stage 1
// ENV: HAKMEM_TINY_TENSION_DRAIN_ENABLE=0 to disable (default=1)
// ENV: HAKMEM_TINY_TENSION_DRAIN_THRESHOLD=N to set threshold (default=1024)
{
// Priority-2: Use cached ENV
int tension_drain_enabled = HAK_ENV_TINY_TENSION_DRAIN_ENABLE();
uint32_t tension_threshold = (uint32_t)HAK_ENV_TINY_TENSION_DRAIN_THRESHOLD();
if (tension_drain_enabled) {
extern __thread TinyTLSSLL g_tls_sll[TINY_NUM_CLASSES];
extern uint32_t tiny_tls_sll_drain(int class_idx, uint32_t batch_size);
uint32_t sll_count = (class_idx < TINY_NUM_CLASSES) ? g_tls_sll[class_idx].count : 0;
if (sll_count >= tension_threshold) {
// Drain all blocks to maximize EMPTY slot creation
uint32_t drained = tiny_tls_sll_drain(class_idx, 0); // 0 = drain all
if (drained > 0) {
// Retry Stage 1 (EMPTY reuse) after drain
// Some slabs might have become EMPTY (meta->used == 0)
goto stage1_retry_after_tension_drain;
}
}
}
}
// ========== Stage 3: Mutex-protected fallback (new SuperSlab allocation) ==========
// All existing SuperSlabs have no UNUSED slots → need new SuperSlab
// P0 instrumentation: count lock acquisitions
lock_stats_init();
if (g_lock_stats_enabled == 1) {
atomic_fetch_add(&g_lock_acquire_count, 1);
atomic_fetch_add(&g_lock_acquire_slab_count, 1);
}
pthread_mutex_lock(&g_shared_pool.alloc_lock);
// Performance measurement: count Stage 3 lock acquisitions
if (__builtin_expect(sp_measure_enabled(), 0)) {
atomic_fetch_add_explicit(&g_sp_stage3_lock_acquired_global,
1, memory_order_relaxed);
atomic_fetch_add_explicit(&g_sp_alloc_lock_contention_global,
1, memory_order_relaxed);
atomic_fetch_add_explicit(&g_sp_stage3_lock_acquired_by_class[class_idx],
1, memory_order_relaxed);
}
// bench プロファイルでは Shared Pool の論理上限を軽くかけておく
uint32_t total_limit = shared_pool_effective_total_slots();
if (total_limit > 0 && g_shared_pool.total_count >= total_limit) {
if (g_lock_stats_enabled == 1) {
atomic_fetch_add(&g_lock_release_count, 1);
}
pthread_mutex_unlock(&g_shared_pool.alloc_lock);
return -1;
}
uint32_t class_limit = shared_pool_effective_class_slots(class_idx);
if (class_limit > 0 &&
class_idx < TINY_NUM_CLASSES_SS &&
(uint32_t)g_shared_pool.class_active_slots[class_idx] >= class_limit) {
if (g_lock_stats_enabled == 1) {
atomic_fetch_add(&g_lock_release_count, 1);
}
pthread_mutex_unlock(&g_shared_pool.alloc_lock);
return -1;
}
// ========== Stage 3: Get new SuperSlab ==========
// Try LRU cache first, then mmap
SuperSlab* new_ss = NULL;
// Stage 3a: Try LRU cache
extern SuperSlab* hak_ss_lru_pop(uint8_t size_class);
int from_lru = 0;
if (class_idx != 7) {
new_ss = hak_ss_lru_pop((uint8_t)class_idx);
from_lru = (new_ss != NULL);
} else {
// C7: Stage3 LRU 再利用は一旦封じる(再利用が汚染源かを切り分ける)
atomic_store_explicit(&g_c7_stage3_magic_ss, 0, memory_order_relaxed);
}
// Stage 3b: If LRU miss, allocate new SuperSlab
if (!new_ss) {
if (!ss_budget_on_alloc(class_idx)) {
if (g_lock_stats_enabled == 1) {
atomic_fetch_add(&g_lock_release_count, 1);
}
pthread_mutex_unlock(&g_shared_pool.alloc_lock);
return -1;
}
// Release the alloc_lock to avoid deadlock with registry during superslab_allocate
if (g_lock_stats_enabled == 1) {
atomic_fetch_add(&g_lock_release_count, 1);
}
pthread_mutex_unlock(&g_shared_pool.alloc_lock);
SuperSlab* allocated_ss = sp_internal_allocate_superslab(class_idx);
// Re-acquire the alloc_lock
if (g_lock_stats_enabled == 1) {
atomic_fetch_add(&g_lock_acquire_count, 1);
atomic_fetch_add(&g_lock_acquire_slab_count, 1); // This is part of acquisition path
}
sp_lock_with_stats(class_idx);
if (!allocated_ss) {
// Allocation failed; return now.
if (g_lock_stats_enabled == 1) {
atomic_fetch_add(&g_lock_release_count, 1);
}
pthread_mutex_unlock(&g_shared_pool.alloc_lock);
return -1; // Out of memory
}
new_ss = allocated_ss;
if (class_idx == 7) {
// Stage3 経由の C7 Superslab は新規確保のみmagic もリセット扱い)
atomic_store_explicit(&g_c7_stage3_magic_ss, 0, memory_order_relaxed);
}
// Add newly allocated SuperSlab to the shared pool's internal array
if (g_shared_pool.total_count >= g_shared_pool.capacity) {
shared_pool_ensure_capacity_unlocked(g_shared_pool.total_count + 1);
if (g_shared_pool.total_count >= g_shared_pool.capacity) {
// Pool table expansion failed; leave ss alive (registry-owned),
// but do not treat it as part of shared_pool.
// This is a critical error, return early.
if (g_lock_stats_enabled == 1) {
atomic_fetch_add(&g_lock_release_count, 1);
}
pthread_mutex_unlock(&g_shared_pool.alloc_lock);
return -1;
}
}
g_shared_pool.slabs[g_shared_pool.total_count] = new_ss;
g_shared_pool.total_count++;
}
// Stage3 から返す前に、LRU 再利用分は必ず空スラブ化する。
// C7 以外でも from_lru の場合は全スラブをリセットしておく。
if (new_ss && (from_lru || class_idx == 7)) {
sp_reset_superslab_all_slabs(new_ss, class_idx, from_lru);
}
#if !HAKMEM_BUILD_RELEASE
if (dbg_acquire == 1 && new_ss) {
fprintf(stderr, "[SP_ACQUIRE_STAGE3] class=%d new SuperSlab (ss=%p from_lru=%d)\n",
class_idx, (void*)new_ss, from_lru);
}
#endif
if (!new_ss) {
if (g_lock_stats_enabled == 1) {
atomic_fetch_add(&g_lock_release_count, 1);
}
pthread_mutex_unlock(&g_shared_pool.alloc_lock);
return -1; // ❌ Out of memory
}
// Before creating a new SuperSlab, consult learning-layer soft cap.
// Phase 9-2: Soft Cap removed to allow Shared Pool to fully replace Legacy Backend.
// We now rely on LRU eviction and EMPTY recycling to manage memory pressure.
// Create metadata for this new SuperSlab
SharedSSMeta* new_meta = sp_meta_find_or_create(new_ss);
if (!new_meta) {
if (g_lock_stats_enabled == 1) {
atomic_fetch_add(&g_lock_release_count, 1);
}
pthread_mutex_unlock(&g_shared_pool.alloc_lock);
return -1; // ❌ Metadata allocation failed
}
// Assign first slot to this class
int first_slot = 0;
if (sp_slot_mark_active(new_meta, first_slot, class_idx) != 0) {
if (g_lock_stats_enabled == 1) {
atomic_fetch_add(&g_lock_release_count, 1);
}
pthread_mutex_unlock(&g_shared_pool.alloc_lock);
return -1; // ❌ Should not happen
}
// Update SuperSlab metadata
new_ss->slab_bitmap |= (1u << first_slot);
ss_slab_meta_class_idx_set(new_ss, first_slot, (uint8_t)class_idx);
new_ss->active_slabs = 1;
g_shared_pool.active_count++;
if (class_idx < TINY_NUM_CLASSES_SS) {
g_shared_pool.class_active_slots[class_idx]++;
}
// Update hint
g_shared_pool.class_hints[class_idx] = new_ss;
*ss_out = new_ss;
*slab_idx_out = first_slot;
if (c7_reset_and_log_if_needed(new_ss, first_slot, class_idx) != 0) {
*ss_out = NULL;
*slab_idx_out = -1;
if (g_lock_stats_enabled == 1) {
atomic_fetch_add(&g_lock_release_count, 1);
}
pthread_mutex_unlock(&g_shared_pool.alloc_lock);
return -1;
}
sp_fix_geometry_if_needed(new_ss, first_slot, class_idx);
if (g_lock_stats_enabled == 1) {
atomic_fetch_add(&g_lock_release_count, 1);
}
pthread_mutex_unlock(&g_shared_pool.alloc_lock);
if (g_sp_stage_stats_enabled) {
atomic_fetch_add(&g_sp_stage3_hits[class_idx], 1);
}
return 0; // ✅ Stage 3 success
}
// ============================================================================
// Performance Measurement: Print Statistics
// ============================================================================
void shared_pool_print_measurements(void) {
if (!sp_measure_enabled()) {
return; // Measurement disabled
}
uint64_t stage2 = atomic_load_explicit(&g_sp_stage2_lock_acquired_global,
memory_order_relaxed);
uint64_t stage3 = atomic_load_explicit(&g_sp_stage3_lock_acquired_global,
memory_order_relaxed);
uint64_t total_locks = atomic_load_explicit(&g_sp_alloc_lock_contention_global,
memory_order_relaxed);
if (total_locks == 0) {
fprintf(stderr, "\n========================================\n");
fprintf(stderr, "Shared Pool Contention Statistics\n");
fprintf(stderr, "========================================\n");
fprintf(stderr, "No lock acquisitions recorded\n");
fprintf(stderr, "========================================\n\n");
return;
}
double stage2_pct = (100.0 * stage2) / total_locks;
double stage3_pct = (100.0 * stage3) / total_locks;
fprintf(stderr, "\n========================================\n");
fprintf(stderr, "Shared Pool Contention Statistics\n");
fprintf(stderr, "========================================\n");
fprintf(stderr, "Stage 2 Locks: %llu (%.1f%%)\n",
(unsigned long long)stage2, stage2_pct);
fprintf(stderr, "Stage 3 Locks: %llu (%.1f%%)\n",
(unsigned long long)stage3, stage3_pct);
fprintf(stderr, "Total Contention: %llu lock acquisitions\n",
(unsigned long long)total_locks);
// Per-class breakdownTiny 用クラス 0-7、特に C5C7 を観測)
fprintf(stderr, "\nPer-class Shared Pool Locks (Stage2/Stage3):\n");
for (int cls = 0; cls < TINY_NUM_CLASSES_SS; cls++) {
uint64_t s2c = atomic_load_explicit(
&g_sp_stage2_lock_acquired_by_class[cls],
memory_order_relaxed);
uint64_t s3c = atomic_load_explicit(
&g_sp_stage3_lock_acquired_by_class[cls],
memory_order_relaxed);
uint64_t tc = s2c + s3c;
if (tc == 0) {
continue; // ロック取得のないクラスは省略
}
fprintf(stderr,
" C%d: Stage2=%llu Stage3=%llu Total=%llu\n",
cls,
(unsigned long long)s2c,
(unsigned long long)s3c,
(unsigned long long)tc);
}
fprintf(stderr, "========================================\n\n");
}