fix(Phase 11+): Cold Start lazy init for unified_cache_refill

Root cause: unified_cache_refill() accessed cache->slots before initialization
when a size class was first used via the refill path (not pop path).

Fix: Add lazy initialization check at start of unified_cache_refill()
- Check if cache->slots is NULL before accessing
- Call unified_cache_init() if needed
- Return NULL if init fails (graceful degradation)

Also includes:
- ss_cold_start_box.inc.h: Box Pattern for default prewarm settings
- hakmem_super_registry.c: Use static array in prewarm (avoid recursion)
- Default prewarm enabled (1 SuperSlab/class, configurable via ENV)

Test: 8B→16B→Mixed allocation pattern now works correctly

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Moe Charm (CI)
2025-12-02 19:43:23 +09:00
parent 644e3c30d1
commit daddbc926c
3 changed files with 163 additions and 16 deletions

View File

@ -0,0 +1,118 @@
// ss_cold_start_box.inc.h - SuperSlab Cold Start Protection
// ==========================================================================
//
// Purpose: Prevent OOM on cold start by enabling minimal prewarm by default
//
// Problem:
// - SuperSlab LRU cache starts empty
// - First allocation triggers mmap() which may fail
// - No prewarm by default = cold start OOM
//
// Solution (Box Pattern):
// - Default: Prewarm 1 SuperSlab per class (minimal footprint: 8MB)
// - ENV configurable: HAKMEM_SS_PREWARM_COUNT=N (0 to disable)
// - Observable: Debug logging
// - Inline: Zero runtime overhead for config access
//
// ENV Variables:
// HAKMEM_SS_PREWARM_COUNT=N - SuperSlabs per class (default: 1)
// HAKMEM_SS_PREWARM_DISABLE=1 - Disable prewarm entirely (legacy compat)
//
// Memory Impact:
// Default (count=1): 8 classes * 1MB = 8MB
// Aggressive (count=4): 8 classes * 4MB = 32MB
//
// ==========================================================================
#ifndef HAK_SS_COLD_START_BOX_INC_H
#define HAK_SS_COLD_START_BOX_INC_H
#include <stdlib.h>
#include <stdio.h> // For ss_cold_start_log_config()
// ============================================================================
// Configuration Constants
// ============================================================================
// Default prewarm count per class (minimal cold start protection)
// Phase 11+: Fixed malloc() recursion by using static array in hak_ss_prewarm_class()
#define SS_COLD_START_DEFAULT_COUNT 1
// Maximum prewarm count per class (safety cap)
#define SS_COLD_START_MAX_COUNT 512
// ============================================================================
// Inline Configuration Helper
// ============================================================================
// Get cold start prewarm count (cached, ENV-configurable)
//
// Returns:
// - 0 if disabled (HAKMEM_SS_PREWARM_DISABLE=1)
// - N from HAKMEM_SS_PREWARM_COUNT=N if set
// - SS_COLD_START_DEFAULT_COUNT (1) otherwise
//
// Thread-safe: uses static cache with simple atomic semantics
//
static inline int ss_cold_start_get_count(void) {
static int g_cached = -1; // -1 = not initialized
if (__builtin_expect(g_cached >= 0, 1)) {
return g_cached; // Fast path: already cached
}
// Slow path: parse ENV
// Check disable flag first
const char* disable_env = getenv("HAKMEM_SS_PREWARM_DISABLE");
if (disable_env && *disable_env && *disable_env != '0') {
g_cached = 0; // Disabled
return 0;
}
// Check custom count
const char* count_env = getenv("HAKMEM_SS_PREWARM_COUNT");
if (count_env && *count_env) {
char* endptr;
long count = strtol(count_env, &endptr, 10);
if (*endptr == '\0' && count >= 0) {
// Cap at max
if (count > SS_COLD_START_MAX_COUNT) {
count = SS_COLD_START_MAX_COUNT;
}
g_cached = (int)count;
return g_cached;
}
// Invalid format - fall through to default
}
// Default: minimal prewarm enabled
g_cached = SS_COLD_START_DEFAULT_COUNT;
return g_cached;
}
// Check if cold start prewarm is enabled
static inline int ss_cold_start_is_enabled(void) {
return ss_cold_start_get_count() > 0;
}
// ============================================================================
// Debug Helper
// ============================================================================
// Log cold start configuration (call from init for diagnostics)
static inline void ss_cold_start_log_config(void) {
#if !HAKMEM_BUILD_RELEASE
int count = ss_cold_start_get_count();
if (count > 0) {
fprintf(stderr, "[SS_COLD_START] Enabled: %d SuperSlabs per class (total: %d MB)\n",
count, count * 8); // 8 classes * 1MB
} else {
fprintf(stderr, "[SS_COLD_START] Disabled\n");
}
#else
(void)0; // No-op in release mode
#endif
}
#endif // HAK_SS_COLD_START_BOX_INC_H

View File

@ -303,6 +303,15 @@ void* unified_cache_refill(int class_idx) {
TinyUnifiedCache* cache = &g_unified_cache[class_idx]; TinyUnifiedCache* cache = &g_unified_cache[class_idx];
// ✅ Phase 11+: Ensure cache is initialized (lazy init for cold path)
if (!cache->slots) {
unified_cache_init();
// Re-check after init (may fail due to alloc failure)
if (!cache->slots) {
return NULL;
}
}
// Step 2: Calculate available room in unified cache // Step 2: Calculate available room in unified cache
int room = (int)cache->capacity - 1; // Leave 1 slot for full detection int room = (int)cache->capacity - 1; // Leave 1 slot for full detection
if (cache->head > cache->tail) { if (cache->head > cache->tail) {

View File

@ -2,6 +2,7 @@
#include "hakmem_tiny_superslab.h" #include "hakmem_tiny_superslab.h"
#include "box/ss_allocation_box.h" // For superslab_allocate() declaration #include "box/ss_allocation_box.h" // For superslab_allocate() declaration
#include "box/ss_addr_map_box.h" // Phase 9-1: SuperSlab address map #include "box/ss_addr_map_box.h" // Phase 9-1: SuperSlab address map
#include "box/ss_cold_start_box.inc.h" // Phase 11+: Cold Start prewarm defaults
#include <string.h> #include <string.h>
#include <stdio.h> #include <stdio.h>
#include <sys/mman.h> // munmap for incompatible SuperSlab eviction #include <sys/mman.h> // munmap for incompatible SuperSlab eviction
@ -587,11 +588,12 @@ void hak_ss_prewarm_class(int size_class, uint32_t count) {
hak_ss_lru_init(); hak_ss_lru_init();
} }
// Allocate all SuperSlabs first (store in temp array to avoid LRU pop/push cycle) // Phase 11+: Use static array to avoid malloc() during init (causes recursion)
SuperSlab** slabs = (SuperSlab**)malloc(count * sizeof(SuperSlab*)); // Cap at 512 as defined in SS_COLD_START_MAX_COUNT
if (!slabs) { #define SS_PREWARM_MAX_BATCH 512
fprintf(stderr, "[SS_PREWARM] Failed to allocate temp array for class %d\n", size_class); static SuperSlab* slabs[SS_PREWARM_MAX_BATCH];
return; if (count > SS_PREWARM_MAX_BATCH) {
count = SS_PREWARM_MAX_BATCH;
} }
// Enable prewarm bypass to prevent LRU cache from being used during allocation // Enable prewarm bypass to prevent LRU cache from being used during allocation
@ -625,7 +627,7 @@ void hak_ss_prewarm_class(int size_class, uint32_t count) {
} }
} }
free(slabs); // Note: slabs is static array, no free() needed
// Debug logging for PREWARM // Debug logging for PREWARM
if (dbg == 1) { if (dbg == 1) {
@ -653,6 +655,7 @@ void hak_ss_prewarm_all(const uint32_t counts[TINY_NUM_CLASSES]) {
} }
// Prewarm: Allocate SuperSlabs at startup and add to LRU cache // Prewarm: Allocate SuperSlabs at startup and add to LRU cache
// Phase 11+: Cold Start Box enables prewarm by default (1 SuperSlab/class)
void hak_ss_prewarm_init(void) { void hak_ss_prewarm_init(void) {
#if !HAKMEM_BUILD_RELEASE #if !HAKMEM_BUILD_RELEASE
// Debug logging flag (lazy init) // Debug logging flag (lazy init)
@ -665,20 +668,37 @@ void hak_ss_prewarm_init(void) {
static const int dbg = 0; static const int dbg = 0;
#endif #endif
// Parse environment variable // Phase 11+: Get default from Cold Start Box (enables prewarm by default)
const char* env = getenv("HAKMEM_PREWARM_SUPERSLABS"); // Can be disabled via HAKMEM_SS_PREWARM_DISABLE=1 or HAKMEM_SS_PREWARM_COUNT=0
if (!env || !*env) { int cold_start_count = ss_cold_start_get_count();
// Prewarm disabled ss_cold_start_log_config(); // Log configuration for diagnostics
if (cold_start_count == 0) {
// Prewarm explicitly disabled
return; return;
} }
// Parse as single number (uniform across all classes) // Check for legacy ENV override (HAKMEM_PREWARM_SUPERSLABS)
// This takes precedence over Cold Start Box default
const char* env = getenv("HAKMEM_PREWARM_SUPERSLABS");
long global = cold_start_count; // Default from Cold Start Box
if (env && *env) {
// Legacy ENV override
char* endptr; char* endptr;
long global = strtol(env, &endptr, 10); long parsed = strtol(env, &endptr, 10);
if (*endptr != '\0' || global <= 0) { if (*endptr == '\0' && parsed >= 0) {
fprintf(stderr, "[SS_PREWARM] Invalid HAKMEM_PREWARM_SUPERSLABS='%s' (expected positive integer)\n", env); global = parsed;
if (global == 0) {
// Legacy disable via HAKMEM_PREWARM_SUPERSLABS=0
return; return;
} }
} else {
fprintf(stderr, "[SS_PREWARM] Invalid HAKMEM_PREWARM_SUPERSLABS='%s' (expected integer)\n", env);
// Continue with cold_start_count default
global = cold_start_count;
}
}
// Cap at reasonable limit (avoid OOM on typo like "10000") // Cap at reasonable limit (avoid OOM on typo like "10000")
if (global > 512) { if (global > 512) {