P2: TLS SLL Redesign - class_map default, tls_cached tracking, conditional header restore

This commit completes the P2 phase of the Tiny Pool TLS SLL redesign to fix the
Header/Next pointer conflict that was causing ~30% crash rates.

Changes:
- P2.1: Make class_map lookup the default (ENV: HAKMEM_TINY_NO_CLASS_MAP=1 for legacy)
- P2.2: Add meta->tls_cached field to track blocks cached in TLS SLL
- P2.3: Make Header restoration conditional in tiny_next_store() (default: skip)
- P2.4: Add invariant verification functions (active + tls_cached ≈ used)
- P0.4: Document new ENV variables in ENV_VARS.md

New ENV variables:
- HAKMEM_TINY_ACTIVE_TRACK=1: Enable active/tls_cached tracking (~1% overhead)
- HAKMEM_TINY_NO_CLASS_MAP=1: Disable class_map (legacy mode)
- HAKMEM_TINY_RESTORE_HEADER=1: Force header restoration (legacy mode)
- HAKMEM_TINY_INVARIANT_CHECK=1: Enable invariant verification (debug)
- HAKMEM_TINY_INVARIANT_DUMP=1: Enable periodic state dumps (debug)

Benchmark results (bench_tiny_hot_hakmem 64B):
- Default (class_map ON): 84.49 M ops/sec
- ACTIVE_TRACK=1: 83.62 M ops/sec (-1%)
- NO_CLASS_MAP=1 (legacy): 85.06 M ops/sec
- MT performance: +21-28% vs system allocator

No crashes observed. All tests passed.

🤖 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-11-28 14:11:37 +09:00
parent 6b86c60a20
commit a6e681aae7
8 changed files with 154 additions and 17 deletions

View File

@ -34,6 +34,7 @@
#include <stdint.h>
#include <string.h>
#include <stdlib.h> // P2.3: for getenv()
#include "hakmem_build_flags.h"
#include "tiny_region_id.h" // HEADER_MAGIC/HEADER_CLASS_MASK for header repair/logging
#include "hakmem_super_registry.h" // hak_super_lookup
@ -74,20 +75,27 @@ static inline __attribute__((always_inline)) void* tiny_next_load(const void* ba
}
// Safe store of next pointer into a block base.
// DESIGN RULE: "Header is written by BOTH Alloc and Free/Drain"
// - Free/Drain paths: This function restores header for C0-C6 (offset 1), then writes Next pointer
// - Alloc paths: Write header before returning block to user (HAK_RET_ALLOC)
// - C7 (offset 0): Header is overwritten by next pointer, so no restoration needed
// P2.3: Header restoration is now conditional (default: skip when class_map is active)
// - When class_map is used for class_idx lookup (default), header restoration is unnecessary
// - Alloc path always writes fresh header before returning block to user (HAK_RET_ALLOC)
// - ENV: HAKMEM_TINY_RESTORE_HEADER=1 to force header restoration (legacy mode)
// P0.1: C7 uses offset 0 (overwrites header), C0-C6 use offset 1 (header preserved)
static inline __attribute__((always_inline)) void tiny_next_store(void* base, int class_idx, void* next) {
size_t off = tiny_next_off(class_idx);
#if HAKMEM_TINY_HEADER_CLASSIDX
// For C0-C6 (offset 1): Restore header before writing next pointer
// For C7 (offset 0): Header is overwritten, so no restoration needed
// P2.3: Skip header restoration by default (class_map is now default for class_idx lookup)
// ENV: HAKMEM_TINY_RESTORE_HEADER=1 to force header restoration (legacy fallback mode)
if (off != 0) {
// Restore header for classes that preserve it (C0-C6)
*(uint8_t*)base = HEADER_MAGIC | (class_idx & HEADER_CLASS_MASK);
static int g_restore_header = -1;
if (__builtin_expect(g_restore_header == -1, 0)) {
const char* e = getenv("HAKMEM_TINY_RESTORE_HEADER");
g_restore_header = (e && *e && *e != '0') ? 1 : 0;
}
if (__builtin_expect(g_restore_header, 0)) {
// Legacy mode: Restore header for classes that preserve it (C0-C6)
*(uint8_t*)base = HEADER_MAGIC | (class_idx & HEADER_CLASS_MASK);
}
}
#endif