Files
hakmem/core/tiny_superslab_free.inc.h

367 lines
19 KiB
C
Raw Normal View History

// tiny_superslab_free.inc.h - SuperSlab Free Layer
// Purpose: Same-thread and cross-thread free handling
// Extracted from: hakmem_tiny_free.inc lines 1171-1475
// Box Theory: Box 6 (Free Fast Path) + Box 2 (Remote Queue) integration
//
// Public functions:
// - hak_tiny_free_superslab(): Main SuperSlab free entry point
// Phase 6.22-B: SuperSlab fast free path
static inline void hak_tiny_free_superslab(void* ptr, SuperSlab* ss) {
CRITICAL FIX: TLS 未初期化による 4T SEGV を完全解消 **問題:** - Larson 4T で 100% SEGV (1T は 2.09M ops/s で完走) - System/mimalloc は 4T で 33.52M ops/s 正常動作 - SS OFF + Remote OFF でも 4T で SEGV **根本原因: (Task agent ultrathink 調査結果)** ``` CRASH: mov (%r15),%r13 R15 = 0x6261 ← ASCII "ba" (ゴミ値、未初期化TLS) ``` Worker スレッドの TLS 変数が未初期化: - `__thread void* g_tls_sll_head[TINY_NUM_CLASSES];` ← 初期化なし - pthread_create() で生成されたスレッドでゼロ初期化されない - NULL チェックが通過 (0x6261 != NULL) → dereference → SEGV **修正内容:** 全 TLS 配列に明示的初期化子 `= {0}` を追加: 1. **core/hakmem_tiny.c:** - `g_tls_sll_head[TINY_NUM_CLASSES] = {0}` - `g_tls_sll_count[TINY_NUM_CLASSES] = {0}` - `g_tls_live_ss[TINY_NUM_CLASSES] = {0}` - `g_tls_bcur[TINY_NUM_CLASSES] = {0}` - `g_tls_bend[TINY_NUM_CLASSES] = {0}` 2. **core/tiny_fastcache.c:** - `g_tiny_fast_cache[TINY_FAST_CLASS_COUNT] = {0}` - `g_tiny_fast_count[TINY_FAST_CLASS_COUNT] = {0}` - `g_tiny_fast_free_head[TINY_FAST_CLASS_COUNT] = {0}` - `g_tiny_fast_free_count[TINY_FAST_CLASS_COUNT] = {0}` 3. **core/hakmem_tiny_magazine.c:** - `g_tls_mags[TINY_NUM_CLASSES] = {0}` 4. **core/tiny_sticky.c:** - `g_tls_sticky_ss[TINY_NUM_CLASSES][TINY_STICKY_RING] = {0}` - `g_tls_sticky_idx[TINY_NUM_CLASSES][TINY_STICKY_RING] = {0}` - `g_tls_sticky_pos[TINY_NUM_CLASSES] = {0}` **効果:** ``` Before: 1T: 2.09M ✅ | 4T: SEGV 💀 After: 1T: 2.41M ✅ | 4T: 4.19M ✅ (+15% 1T, SEGV解消) ``` **テスト:** ```bash # 1 thread: 完走 ./larson_hakmem 2 8 128 1024 1 12345 1 → Throughput = 2,407,597 ops/s ✅ # 4 threads: 完走(以前は SEGV) ./larson_hakmem 2 8 128 1024 1 12345 4 → Throughput = 4,192,155 ops/s ✅ ``` **調査協力:** Task agent (ultrathink mode) による完璧な根本原因特定 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-07 01:27:04 +09:00
// Route trace: count SuperSlab free entries (diagnostics only)
extern _Atomic uint64_t g_free_ss_enter;
atomic_fetch_add_explicit(&g_free_ss_enter, 1, memory_order_relaxed);
ROUTE_MARK(16); // free_enter
HAK_DBG_INC(g_superslab_free_count); // Phase 7.6: Track SuperSlab frees
// Get slab index (supports 1MB/2MB SuperSlabs)
int slab_idx = slab_index_for(ss, ptr);
size_t ss_size = (size_t)1ULL << ss->lg_size;
uintptr_t ss_base = (uintptr_t)ss;
if (__builtin_expect(slab_idx < 0, 0)) {
uintptr_t aux = tiny_remote_pack_diag(0xBAD1u, ss_base, ss_size, (uintptr_t)ptr);
tiny_debug_ring_record(TINY_RING_EVENT_REMOTE_INVALID, (uint16_t)ss->size_class, ptr, aux);
if (g_tiny_safe_free_strict) { raise(SIGUSR2); return; }
return;
}
TinySlabMeta* meta = &ss->slabs[slab_idx];
// Normalize to block base for header classes (C0-C6)
void* base = (ss->size_class == 7) ? ptr : (void*)((uint8_t*)ptr - 1);
// Debug: Log first C7 alloc/free for path verification
if (ss->size_class == 7) {
static _Atomic int c7_free_count = 0;
int count = atomic_fetch_add_explicit(&c7_free_count, 1, memory_order_relaxed);
if (count == 0) {
fprintf(stderr, "[C7_FIRST_FREE] ptr=%p base=%p slab_idx=%d\n", ptr, base, slab_idx);
}
}
if (__builtin_expect(tiny_remote_watch_is(ptr), 0)) {
tiny_remote_watch_note("free_enter", ss, slab_idx, ptr, 0xA240u, tiny_self_u32(), 0);
extern __thread TinyTLSSlab g_tls_slabs[];
tiny_alloc_dump_tls_state(ss->size_class, "watch_free_enter", &g_tls_slabs[ss->size_class]);
#if !HAKMEM_BUILD_RELEASE
extern __thread TinyTLSMag g_tls_mags[];
TinyTLSMag* watch_mag = &g_tls_mags[ss->size_class];
fprintf(stderr,
"[REMOTE_WATCH_MAG] cls=%u mag_top=%d cap=%d\n",
ss->size_class,
watch_mag->top,
watch_mag->cap);
#endif
}
// BUGFIX: Validate size_class before using as array index (prevents OOB)
if (__builtin_expect(ss->size_class < 0 || ss->size_class >= TINY_NUM_CLASSES, 0)) {
tiny_debug_ring_record(TINY_RING_EVENT_REMOTE_INVALID, 0xF1, ptr, (uintptr_t)ss->size_class);
if (g_tiny_safe_free_strict) { raise(SIGUSR2); return; }
return;
}
Phase 7-1 PoC: Region-ID Direct Lookup (+39%~+436% improvement!) Implemented ultra-fast header-based free path that eliminates SuperSlab lookup bottleneck (100+ cycles → 5-10 cycles). ## Key Changes 1. **Smart Headers** (core/tiny_region_id.h): - 1-byte header before each allocation stores class_idx - Memory layout: [Header: 1B] [User data: N-1B] - Overhead: <2% average (0% for Slab[0] using wasted padding) 2. **Ultra-Fast Allocation** (core/tiny_alloc_fast.inc.h): - Write header at base: *base = class_idx - Return user pointer: base + 1 3. **Ultra-Fast Free** (core/tiny_free_fast_v2.inc.h): - Read class_idx from header (ptr-1): 2-3 cycles - Push base (ptr-1) to TLS freelist: 3-5 cycles - Total: 5-10 cycles (vs 500+ cycles current!) 4. **Free Path Integration** (core/box/hak_free_api.inc.h): - Removed SuperSlab lookup from fast path - Direct header validation (no lookup needed!) 5. **Size Class Adjustment** (core/hakmem_tiny.h): - Max tiny size: 1023B (was 1024B) - 1024B requests → Mid allocator fallback ## Performance Results | Size | Baseline | Phase 7 | Improvement | |------|----------|---------|-------------| | 128B | 1.22M | 6.54M | **+436%** 🚀 | | 512B | 1.22M | 1.70M | **+39%** | | 1023B | 1.22M | 1.92M | **+57%** | ## Build & Test Enable Phase 7: make HEADER_CLASSIDX=1 bench_random_mixed_hakmem Run benchmark: HAKMEM_TINY_USE_SUPERSLAB=1 ./bench_random_mixed_hakmem 10000 128 1234567 ## Known Issues - 1024B requests fallback to Mid allocator (by design) - Target 40-60M ops/s not yet reached (current: 1.7-6.5M) - Further optimization needed (TLS capacity tuning, refill optimization) ## Credits Design: ChatGPT Pro Ultrathink, Claude Code Implementation: Claude Code with Task Agent Ultrathink support 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-08 03:18:17 +09:00
// ChatGPT Pro Optimization: Move safety checks to debug mode only
// In release builds, these checks are completely eliminated by the compiler
// Expected impact: -10~-15% CPU (eliminates O(n) duplicate scan)
#if !HAKMEM_BUILD_RELEASE
if (__builtin_expect(g_tiny_safe_free, 0)) {
size_t blk = g_tiny_class_sizes[ss->size_class];
uint8_t* base = tiny_slab_base_for(ss, slab_idx);
uintptr_t delta = (uintptr_t)ptr - (uintptr_t)base;
int cap_ok = (meta->capacity > 0) ? 1 : 0;
int align_ok = (delta % blk) == 0;
int range_ok = cap_ok && (delta / blk) < meta->capacity;
if (!align_ok || !range_ok) {
uint32_t code = 0xA100u;
if (align_ok) code |= 0x2u;
if (range_ok) code |= 0x1u;
uintptr_t aux = tiny_remote_pack_diag(code, ss_base, ss_size, (uintptr_t)ptr);
tiny_debug_ring_record(TINY_RING_EVENT_REMOTE_INVALID, (uint16_t)ss->size_class, ptr, aux);
if (g_tiny_safe_free_strict) { raise(SIGUSR2); return; }
return;
}
// Duplicate in freelist (best-effort scan up to 64)
Phase 7-1 PoC: Region-ID Direct Lookup (+39%~+436% improvement!) Implemented ultra-fast header-based free path that eliminates SuperSlab lookup bottleneck (100+ cycles → 5-10 cycles). ## Key Changes 1. **Smart Headers** (core/tiny_region_id.h): - 1-byte header before each allocation stores class_idx - Memory layout: [Header: 1B] [User data: N-1B] - Overhead: <2% average (0% for Slab[0] using wasted padding) 2. **Ultra-Fast Allocation** (core/tiny_alloc_fast.inc.h): - Write header at base: *base = class_idx - Return user pointer: base + 1 3. **Ultra-Fast Free** (core/tiny_free_fast_v2.inc.h): - Read class_idx from header (ptr-1): 2-3 cycles - Push base (ptr-1) to TLS freelist: 3-5 cycles - Total: 5-10 cycles (vs 500+ cycles current!) 4. **Free Path Integration** (core/box/hak_free_api.inc.h): - Removed SuperSlab lookup from fast path - Direct header validation (no lookup needed!) 5. **Size Class Adjustment** (core/hakmem_tiny.h): - Max tiny size: 1023B (was 1024B) - 1024B requests → Mid allocator fallback ## Performance Results | Size | Baseline | Phase 7 | Improvement | |------|----------|---------|-------------| | 128B | 1.22M | 6.54M | **+436%** 🚀 | | 512B | 1.22M | 1.70M | **+39%** | | 1023B | 1.22M | 1.92M | **+57%** | ## Build & Test Enable Phase 7: make HEADER_CLASSIDX=1 bench_random_mixed_hakmem Run benchmark: HAKMEM_TINY_USE_SUPERSLAB=1 ./bench_random_mixed_hakmem 10000 128 1234567 ## Known Issues - 1024B requests fallback to Mid allocator (by design) - Target 40-60M ops/s not yet reached (current: 1.7-6.5M) - Further optimization needed (TLS capacity tuning, refill optimization) ## Credits Design: ChatGPT Pro Ultrathink, Claude Code Implementation: Claude Code with Task Agent Ultrathink support 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-08 03:18:17 +09:00
// NOTE: This O(n) scan is VERY expensive (can scan 64 pointers per free!)
void* scan = meta->freelist; int scanned = 0; int dup = 0;
while (scan && scanned < 64) { if (scan == base) { dup = 1; break; } scan = *(void**)scan; scanned++; }
if (dup) {
uintptr_t aux = tiny_remote_pack_diag(0xDFu, ss_base, ss_size, (uintptr_t)ptr);
tiny_debug_ring_record(TINY_RING_EVENT_REMOTE_INVALID, (uint16_t)ss->size_class, ptr, aux);
if (g_tiny_safe_free_strict) { raise(SIGUSR2); return; }
return;
}
}
Phase 7-1 PoC: Region-ID Direct Lookup (+39%~+436% improvement!) Implemented ultra-fast header-based free path that eliminates SuperSlab lookup bottleneck (100+ cycles → 5-10 cycles). ## Key Changes 1. **Smart Headers** (core/tiny_region_id.h): - 1-byte header before each allocation stores class_idx - Memory layout: [Header: 1B] [User data: N-1B] - Overhead: <2% average (0% for Slab[0] using wasted padding) 2. **Ultra-Fast Allocation** (core/tiny_alloc_fast.inc.h): - Write header at base: *base = class_idx - Return user pointer: base + 1 3. **Ultra-Fast Free** (core/tiny_free_fast_v2.inc.h): - Read class_idx from header (ptr-1): 2-3 cycles - Push base (ptr-1) to TLS freelist: 3-5 cycles - Total: 5-10 cycles (vs 500+ cycles current!) 4. **Free Path Integration** (core/box/hak_free_api.inc.h): - Removed SuperSlab lookup from fast path - Direct header validation (no lookup needed!) 5. **Size Class Adjustment** (core/hakmem_tiny.h): - Max tiny size: 1023B (was 1024B) - 1024B requests → Mid allocator fallback ## Performance Results | Size | Baseline | Phase 7 | Improvement | |------|----------|---------|-------------| | 128B | 1.22M | 6.54M | **+436%** 🚀 | | 512B | 1.22M | 1.70M | **+39%** | | 1023B | 1.22M | 1.92M | **+57%** | ## Build & Test Enable Phase 7: make HEADER_CLASSIDX=1 bench_random_mixed_hakmem Run benchmark: HAKMEM_TINY_USE_SUPERSLAB=1 ./bench_random_mixed_hakmem 10000 128 1234567 ## Known Issues - 1024B requests fallback to Mid allocator (by design) - Target 40-60M ops/s not yet reached (current: 1.7-6.5M) - Further optimization needed (TLS capacity tuning, refill optimization) ## Credits Design: ChatGPT Pro Ultrathink, Claude Code Implementation: Claude Code with Task Agent Ultrathink support 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-08 03:18:17 +09:00
#endif // !HAKMEM_BUILD_RELEASE
// Lightweight guard always-on for class7 (headerless, 1024B): prevent corrupted pointer writes in release
if (__builtin_expect(ss->size_class == 7, 0)) {
size_t blk = g_tiny_class_sizes[ss->size_class];
uint8_t* base = tiny_slab_base_for(ss, slab_idx);
uintptr_t delta = (uintptr_t)ptr - (uintptr_t)base;
int cap_ok = (meta->capacity > 0) ? 1 : 0;
int align_ok = (delta % blk) == 0;
int range_ok = cap_ok && (delta / blk) < meta->capacity;
if (!align_ok || !range_ok) {
uintptr_t aux = tiny_remote_pack_diag(0xA107u, ss_base, ss_size, (uintptr_t)ptr);
tiny_debug_ring_record(TINY_RING_EVENT_REMOTE_INVALID, (uint16_t)ss->size_class, ptr, aux);
// Fail-fast in class7 to avoid silent SLL/freelist corruption
raise(SIGUSR2);
return;
}
}
// Phase 6.23: Same-thread check
uint32_t my_tid = tiny_self_u32();
const int debug_guard = g_debug_remote_guard;
static __thread int g_debug_free_count = 0;
CRITICAL FIX: TLS 未初期化による 4T SEGV を完全解消 **問題:** - Larson 4T で 100% SEGV (1T は 2.09M ops/s で完走) - System/mimalloc は 4T で 33.52M ops/s 正常動作 - SS OFF + Remote OFF でも 4T で SEGV **根本原因: (Task agent ultrathink 調査結果)** ``` CRASH: mov (%r15),%r13 R15 = 0x6261 ← ASCII "ba" (ゴミ値、未初期化TLS) ``` Worker スレッドの TLS 変数が未初期化: - `__thread void* g_tls_sll_head[TINY_NUM_CLASSES];` ← 初期化なし - pthread_create() で生成されたスレッドでゼロ初期化されない - NULL チェックが通過 (0x6261 != NULL) → dereference → SEGV **修正内容:** 全 TLS 配列に明示的初期化子 `= {0}` を追加: 1. **core/hakmem_tiny.c:** - `g_tls_sll_head[TINY_NUM_CLASSES] = {0}` - `g_tls_sll_count[TINY_NUM_CLASSES] = {0}` - `g_tls_live_ss[TINY_NUM_CLASSES] = {0}` - `g_tls_bcur[TINY_NUM_CLASSES] = {0}` - `g_tls_bend[TINY_NUM_CLASSES] = {0}` 2. **core/tiny_fastcache.c:** - `g_tiny_fast_cache[TINY_FAST_CLASS_COUNT] = {0}` - `g_tiny_fast_count[TINY_FAST_CLASS_COUNT] = {0}` - `g_tiny_fast_free_head[TINY_FAST_CLASS_COUNT] = {0}` - `g_tiny_fast_free_count[TINY_FAST_CLASS_COUNT] = {0}` 3. **core/hakmem_tiny_magazine.c:** - `g_tls_mags[TINY_NUM_CLASSES] = {0}` 4. **core/tiny_sticky.c:** - `g_tls_sticky_ss[TINY_NUM_CLASSES][TINY_STICKY_RING] = {0}` - `g_tls_sticky_idx[TINY_NUM_CLASSES][TINY_STICKY_RING] = {0}` - `g_tls_sticky_pos[TINY_NUM_CLASSES] = {0}` **効果:** ``` Before: 1T: 2.09M ✅ | 4T: SEGV 💀 After: 1T: 2.41M ✅ | 4T: 4.19M ✅ (+15% 1T, SEGV解消) ``` **テスト:** ```bash # 1 thread: 完走 ./larson_hakmem 2 8 128 1024 1 12345 1 → Throughput = 2,407,597 ops/s ✅ # 4 threads: 完走(以前は SEGV) ./larson_hakmem 2 8 128 1024 1 12345 4 → Throughput = 4,192,155 ops/s ✅ ``` **調査協力:** Task agent (ultrathink mode) による完璧な根本原因特定 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-07 01:27:04 +09:00
// If owner is not set yet, claim ownership to avoid spurious remote path in 1T
if (!g_tiny_force_remote && meta->owner_tid == 0) {
meta->owner_tid = my_tid;
}
if (!g_tiny_force_remote && meta->owner_tid != 0 && meta->owner_tid == my_tid) {
ROUTE_MARK(17); // free_same_thread
// Fast path: Direct freelist push (same-thread)
if (0 && debug_guard && g_debug_free_count < 1) {
fprintf(stderr, "[FREE_SS] SAME-THREAD: owner=%u my=%u\n",
meta->owner_tid, my_tid);
g_debug_free_count++;
}
if (__builtin_expect(meta->used == 0, 0)) {
uintptr_t aux = tiny_remote_pack_diag(0x00u, ss_base, ss_size, (uintptr_t)ptr);
tiny_debug_ring_record(TINY_RING_EVENT_REMOTE_INVALID, (uint16_t)ss->size_class, ptr, aux);
if (g_tiny_safe_free_strict) { raise(SIGUSR2); return; }
return;
}
tiny_remote_track_expect_alloc(ss, slab_idx, ptr, "local_free_enter", my_tid);
if (!tiny_remote_guard_allow_local_push(ss, slab_idx, meta, ptr, "local_free", my_tid)) {
#include "box/free_remote_box.h"
int transitioned = tiny_free_remote_box(ss, slab_idx, meta, base, my_tid);
if (transitioned) {
extern unsigned long long g_remote_free_transitions[];
g_remote_free_transitions[ss->size_class]++;
// Free-side route: remote transition observed
do {
static int g_route_free = -1; if (__builtin_expect(g_route_free == -1, 0)) {
const char* e = getenv("HAKMEM_TINY_ROUTE_FREE");
g_route_free = (e && *e && *e != '0') ? 1 : 0; }
if (g_route_free) route_free_commit((int)ss->size_class, (1ull<<18), 0xE2);
} while (0);
}
return;
}
// Optional: MidTC (TLS tcache for 128..1024B) — allow bypass via env HAKMEM_TINY_FREE_TO_SS=1
do {
static int g_free_to_ss = -1;
if (__builtin_expect(g_free_to_ss == -1, 0)) {
const char* e = getenv("HAKMEM_TINY_FREE_TO_SS");
g_free_to_ss = (e && *e && *e != '0') ? 1 : 0; // default OFF
}
if (!g_free_to_ss) {
int cls = (int)ss->size_class;
if (midtc_enabled() && cls >= 4) {
if (midtc_push(cls, base)) {
// Treat as returned to TLS cache (not SS freelist)
meta->used--;
ss_active_dec_one(ss);
return;
}
}
}
} while (0);
#include "box/free_local_box.h"
// Perform freelist push (+first-free publish if applicable)
void* prev_before = meta->freelist;
tiny_free_local_box(ss, slab_idx, meta, base, my_tid);
if (prev_before == NULL) {
ROUTE_MARK(19); // first_free_transition
extern unsigned long long g_first_free_transitions[];
g_first_free_transitions[ss->size_class]++;
ROUTE_MARK(20); // mailbox_publish
// Free-side route commit (one-shot)
do {
static int g_route_free = -1; if (__builtin_expect(g_route_free == -1, 0)) {
const char* e = getenv("HAKMEM_TINY_ROUTE_FREE");
g_route_free = (e && *e && *e != '0') ? 1 : 0; }
int cls = (int)ss->size_class;
if (g_route_free) route_free_commit(cls, (1ull<<19) | (1ull<<20), 0xE1);
} while (0);
}
if (__builtin_expect(debug_guard, 0)) {
fprintf(stderr, "[REMOTE_LOCAL] cls=%u slab=%d owner=%u my=%u ptr=%p prev=%p used=%u\n",
ss->size_class, slab_idx, meta->owner_tid, my_tid, ptr, prev_before, meta->used);
}
// 空検出は別途(ホットパス除外)
} else {
ROUTE_MARK(18); // free_remote_transition
if (__builtin_expect(meta->owner_tid == my_tid && meta->owner_tid == 0, 0)) {
uintptr_t aux = tiny_remote_pack_diag(0xA300u, ss_base, ss_size, (uintptr_t)ptr);
tiny_debug_ring_record(TINY_RING_EVENT_REMOTE_INVALID, (uint16_t)ss->size_class, ptr, aux);
if (debug_guard) {
fprintf(stderr, "[REMOTE_OWNER_ZERO] cls=%u slab=%d ptr=%p my=%u used=%u\n",
ss->size_class, slab_idx, ptr, my_tid, (unsigned)meta->used);
}
}
tiny_remote_track_expect_alloc(ss, slab_idx, ptr, "remote_free_enter", my_tid);
// Slow path: Remote free (cross-thread)
if (0 && debug_guard && g_debug_free_count < 5) {
fprintf(stderr, "[FREE_SS] CROSS-THREAD: owner=%u my=%u slab_idx=%d\n",
meta->owner_tid, my_tid, slab_idx);
g_debug_free_count++;
}
if (__builtin_expect(g_tiny_safe_free, 0)) {
// Best-effort duplicate scan in remote stack (up to 64 nodes)
uintptr_t head = atomic_load_explicit(&ss->remote_heads[slab_idx], memory_order_acquire);
uintptr_t base = ss_base;
int scanned = 0; int dup = 0;
uintptr_t cur = head;
while (cur && scanned < 64) {
if ((cur < base) || (cur >= base + ss_size)) {
uintptr_t aux = tiny_remote_pack_diag(0xA200u, base, ss_size, cur);
tiny_debug_ring_record(TINY_RING_EVENT_REMOTE_INVALID, (uint16_t)ss->size_class, (void*)cur, aux);
if (g_tiny_safe_free_strict) { raise(SIGUSR2); return; }
break;
}
if ((void*)cur == ptr) { dup = 1; break; }
if (__builtin_expect(g_remote_side_enable, 0)) {
if (!tiny_remote_sentinel_ok((void*)cur)) {
uintptr_t aux = tiny_remote_pack_diag(0xA202u, base, ss_size, cur);
tiny_debug_ring_record(TINY_RING_EVENT_REMOTE_INVALID, (uint16_t)ss->size_class, (void*)cur, aux);
uintptr_t observed = atomic_load_explicit((_Atomic uintptr_t*)(void*)cur, memory_order_relaxed);
tiny_remote_report_corruption("scan", (void*)cur, observed);
Tiny: fix remote sentinel leak → SEGV; add defense-in-depth; PoolTLS: refill-boundary remote drain; build UX help; quickstart docs Summary - Fix SEGV root cause in Tiny random_mixed: TINY_REMOTE_SENTINEL leaked from Remote queue into freelist/TLS SLL. - Clear/guard sentinel at the single boundary where Remote merges to freelist. - Add minimal defense-in-depth in freelist_pop and TLS SLL pop. - Silence verbose prints behind debug gates to reduce noise in release runs. - Pool TLS: integrate Remote Queue drain at refill boundary to avoid unnecessary backend carve/OS calls when possible. - DX: strengthen build.sh with help/list/verify and add docs/BUILDING_QUICKSTART.md. Details - core/superslab/superslab_inline.h: guard head/node against TINY_REMOTE_SENTINEL; sanitize node[0] when splicing local chain; only print diagnostics when debug guard is enabled. - core/slab_handle.h: freelist_pop breaks on sentinel head (fail-fast under strict). - core/tiny_alloc_fast_inline.h: TLS SLL pop breaks on sentinel head (rare branch). - core/tiny_superslab_free.inc.h: sentinel scan log behind debug guard. - core/pool_refill.c: try pool_remote_pop_chain() before backend carve in pool_refill_and_alloc(). - core/tiny_adaptive_sizing.c: default adaptive logs off; enable via HAKMEM_ADAPTIVE_LOG=1. - build.sh: add help/list/verify; EXTRA_MAKEFLAGS passthrough; echo pinned flags. - docs/BUILDING_QUICKSTART.md: add one‑pager for targets/flags/env/perf/strace. Verification (high level) - Tiny random_mixed 10k 256/1024: SEGV resolved; runs complete. - Pool TLS 1T/4T perf: HAKMEM >= system (≈ +0.7% 1T, ≈ +2.9% 4T); syscall counts ~10–13. Known issues (to address next) - Tiny random_mixed perf is weak vs system: - 1T/500k/256: cycles/op ≈ 240 vs ~47 (≈5× slower), IPC ≈0.92, branch‑miss ≈11%. - 1T/500k/1024: cycles/op ≈ 149 vs ~53 (≈2.8× slower), IPC ≈0.82, branch‑miss ≈10.5%. - Hypothesis: frequent SuperSlab path for class7 (fast_cap=0), branchy refill/adopt, and hot-path divergence. - Proposed next steps: - Introduce fast_cap>0 for class7 (bounded TLS SLL) and a simpler batch refill. - Add env‑gated Remote Side OFF for 1T A/B (reduce side-table and guards). - Revisit likely/unlikely and unify adopt boundary sequencing (drain→bind→acquire) for Tiny.
2025-11-09 16:49:34 +09:00
if (__builtin_expect(g_debug_remote_guard, 0)) {
fprintf(stderr,
"[REMOTE_SENTINEL] cls=%u slab=%d cur=%p head=%p ptr=%p scanned=%d observed=0x%016" PRIxPTR " owner=%u used=%u freelist=%p remote_head=%p\n",
ss->size_class,
slab_idx,
(void*)cur,
(void*)head,
ptr,
scanned,
observed,
meta->owner_tid,
(unsigned)meta->used,
meta->freelist,
(void*)atomic_load_explicit(&ss->remote_heads[slab_idx], memory_order_relaxed));
}
if (g_tiny_safe_free_strict) { raise(SIGUSR2); return; }
break;
}
cur = tiny_remote_side_get(ss, slab_idx, (void*)cur);
} else {
if ((cur & (uintptr_t)(sizeof(void*) - 1)) != 0) {
uintptr_t aux = tiny_remote_pack_diag(0xA201u, base, ss_size, cur);
tiny_debug_ring_record(TINY_RING_EVENT_REMOTE_INVALID, (uint16_t)ss->size_class, (void*)cur, aux);
if (g_tiny_safe_free_strict) { raise(SIGUSR2); return; }
break;
}
cur = (uintptr_t)(*(void**)(void*)cur);
}
scanned++;
}
if (dup) {
uintptr_t aux = tiny_remote_pack_diag(0xD1u, ss_base, ss_size, (uintptr_t)ptr);
tiny_debug_ring_record(TINY_RING_EVENT_REMOTE_INVALID, (uint16_t)ss->size_class, ptr, aux);
if (g_tiny_safe_free_strict) { raise(SIGUSR2); return; }
return;
}
}
if (__builtin_expect(meta->used == 0, 0)) {
uintptr_t aux = tiny_remote_pack_diag(0x01u, ss_base, ss_size, (uintptr_t)ptr);
tiny_debug_ring_record(TINY_RING_EVENT_REMOTE_INVALID, (uint16_t)ss->size_class, ptr, aux);
if (g_tiny_safe_free_strict) { raise(SIGUSR2); return; }
return;
}
static int g_ss_adopt_en2 = -1; // env cached
if (g_ss_adopt_en2 == -1) {
char* e = getenv("HAKMEM_TINY_SS_ADOPT");
// 既定: Remote Queueを使う1。env指定時のみ上書き。
g_ss_adopt_en2 = (e == NULL) ? 1 : ((*e != '0') ? 1 : 0);
if (__builtin_expect(debug_guard, 0)) {
fprintf(stderr, "[FREE_SS] g_ss_adopt_en2=%d (env='%s')\n", g_ss_adopt_en2, e ? e : "(null)");
}
}
CRITICAL FIX: TLS 未初期化による 4T SEGV を完全解消 **問題:** - Larson 4T で 100% SEGV (1T は 2.09M ops/s で完走) - System/mimalloc は 4T で 33.52M ops/s 正常動作 - SS OFF + Remote OFF でも 4T で SEGV **根本原因: (Task agent ultrathink 調査結果)** ``` CRASH: mov (%r15),%r13 R15 = 0x6261 ← ASCII "ba" (ゴミ値、未初期化TLS) ``` Worker スレッドの TLS 変数が未初期化: - `__thread void* g_tls_sll_head[TINY_NUM_CLASSES];` ← 初期化なし - pthread_create() で生成されたスレッドでゼロ初期化されない - NULL チェックが通過 (0x6261 != NULL) → dereference → SEGV **修正内容:** 全 TLS 配列に明示的初期化子 `= {0}` を追加: 1. **core/hakmem_tiny.c:** - `g_tls_sll_head[TINY_NUM_CLASSES] = {0}` - `g_tls_sll_count[TINY_NUM_CLASSES] = {0}` - `g_tls_live_ss[TINY_NUM_CLASSES] = {0}` - `g_tls_bcur[TINY_NUM_CLASSES] = {0}` - `g_tls_bend[TINY_NUM_CLASSES] = {0}` 2. **core/tiny_fastcache.c:** - `g_tiny_fast_cache[TINY_FAST_CLASS_COUNT] = {0}` - `g_tiny_fast_count[TINY_FAST_CLASS_COUNT] = {0}` - `g_tiny_fast_free_head[TINY_FAST_CLASS_COUNT] = {0}` - `g_tiny_fast_free_count[TINY_FAST_CLASS_COUNT] = {0}` 3. **core/hakmem_tiny_magazine.c:** - `g_tls_mags[TINY_NUM_CLASSES] = {0}` 4. **core/tiny_sticky.c:** - `g_tls_sticky_ss[TINY_NUM_CLASSES][TINY_STICKY_RING] = {0}` - `g_tls_sticky_idx[TINY_NUM_CLASSES][TINY_STICKY_RING] = {0}` - `g_tls_sticky_pos[TINY_NUM_CLASSES] = {0}` **効果:** ``` Before: 1T: 2.09M ✅ | 4T: SEGV 💀 After: 1T: 2.41M ✅ | 4T: 4.19M ✅ (+15% 1T, SEGV解消) ``` **テスト:** ```bash # 1 thread: 完走 ./larson_hakmem 2 8 128 1024 1 12345 1 → Throughput = 2,407,597 ops/s ✅ # 4 threads: 完走(以前は SEGV) ./larson_hakmem 2 8 128 1024 1 12345 4 → Throughput = 4,192,155 ops/s ✅ ``` **調査協力:** Task agent (ultrathink mode) による完璧な根本原因特定 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-07 01:27:04 +09:00
// A/B gate: disable remote MPSC (use legacy freelist push)
do {
static int g_disable_remote = -1;
if (__builtin_expect(g_disable_remote == -1, 0)) {
const char* e = getenv("HAKMEM_TINY_DISABLE_REMOTE");
g_disable_remote = (e && *e && *e != '0') ? 1 : 0;
}
if (__builtin_expect(g_disable_remote, 0)) {
g_ss_adopt_en2 = 0;
}
} while (0);
if (g_ss_adopt_en2) {
// Use remote queue
uintptr_t head_word = __atomic_load_n((uintptr_t*)base, __ATOMIC_RELAXED);
if (debug_guard) fprintf(stderr, "[REMOTE_PUSH_CALL] cls=%u slab=%d owner=%u my=%u ptr=%p used=%u remote_count=%u head=%p word=0x%016" PRIxPTR "\n",
ss->size_class,
slab_idx,
meta->owner_tid,
my_tid,
ptr,
(unsigned)meta->used,
atomic_load_explicit(&ss->remote_counts[slab_idx], memory_order_relaxed),
(void*)atomic_load_explicit(&ss->remote_heads[slab_idx], memory_order_relaxed),
head_word);
int dup_remote = tiny_remote_queue_contains_guard(ss, slab_idx, base);
if (!dup_remote && __builtin_expect(g_remote_side_enable, 0)) {
dup_remote = (head_word == TINY_REMOTE_SENTINEL) || tiny_remote_side_contains(ss, slab_idx, base);
}
if (__builtin_expect(head_word == TINY_REMOTE_SENTINEL && !dup_remote && g_debug_remote_guard, 0)) {
tiny_remote_watch_note("dup_scan_miss", ss, slab_idx, ptr, 0xA215u, my_tid, 0);
}
if (dup_remote) {
uintptr_t aux = tiny_remote_pack_diag(0xA214u, ss_base, ss_size, (uintptr_t)ptr);
tiny_remote_watch_mark(ptr, "dup_prevent", my_tid);
tiny_remote_watch_note("dup_prevent", ss, slab_idx, ptr, 0xA214u, my_tid, 0);
tiny_debug_ring_record(TINY_RING_EVENT_REMOTE_INVALID, (uint16_t)ss->size_class, ptr, aux);
tiny_failfast_abort_ptr("double_free_remote", ss, slab_idx, ptr, "remote_side_contains");
}
if (__builtin_expect(g_remote_side_enable && (head_word & 0xFFFFu) == 0x6261u, 0)) {
// TLS guard scribble detected on the node's first word → same-pointer double free across routes
uintptr_t aux = tiny_remote_pack_diag(0xA213u, ss_base, ss_size, (uintptr_t)ptr);
tiny_debug_ring_record(TINY_RING_EVENT_REMOTE_INVALID, (uint16_t)ss->size_class, ptr, aux);
tiny_remote_watch_mark(ptr, "pre_push", my_tid);
tiny_remote_watch_note("pre_push", ss, slab_idx, ptr, 0xA231u, my_tid, 0);
tiny_remote_report_corruption("pre_push", ptr, head_word);
tiny_failfast_abort_ptr("double_free_scribble", ss, slab_idx, ptr, "scribble_6261");
}
if (__builtin_expect(tiny_remote_watch_is(ptr), 0)) {
tiny_remote_watch_note("free_remote", ss, slab_idx, ptr, 0xA232u, my_tid, 0);
}
int was_empty = ss_remote_push(ss, slab_idx, base); // ss_active_dec_one() called inside
meta->used--;
CRITICAL FIX: TLS 未初期化による 4T SEGV を完全解消 **問題:** - Larson 4T で 100% SEGV (1T は 2.09M ops/s で完走) - System/mimalloc は 4T で 33.52M ops/s 正常動作 - SS OFF + Remote OFF でも 4T で SEGV **根本原因: (Task agent ultrathink 調査結果)** ``` CRASH: mov (%r15),%r13 R15 = 0x6261 ← ASCII "ba" (ゴミ値、未初期化TLS) ``` Worker スレッドの TLS 変数が未初期化: - `__thread void* g_tls_sll_head[TINY_NUM_CLASSES];` ← 初期化なし - pthread_create() で生成されたスレッドでゼロ初期化されない - NULL チェックが通過 (0x6261 != NULL) → dereference → SEGV **修正内容:** 全 TLS 配列に明示的初期化子 `= {0}` を追加: 1. **core/hakmem_tiny.c:** - `g_tls_sll_head[TINY_NUM_CLASSES] = {0}` - `g_tls_sll_count[TINY_NUM_CLASSES] = {0}` - `g_tls_live_ss[TINY_NUM_CLASSES] = {0}` - `g_tls_bcur[TINY_NUM_CLASSES] = {0}` - `g_tls_bend[TINY_NUM_CLASSES] = {0}` 2. **core/tiny_fastcache.c:** - `g_tiny_fast_cache[TINY_FAST_CLASS_COUNT] = {0}` - `g_tiny_fast_count[TINY_FAST_CLASS_COUNT] = {0}` - `g_tiny_fast_free_head[TINY_FAST_CLASS_COUNT] = {0}` - `g_tiny_fast_free_count[TINY_FAST_CLASS_COUNT] = {0}` 3. **core/hakmem_tiny_magazine.c:** - `g_tls_mags[TINY_NUM_CLASSES] = {0}` 4. **core/tiny_sticky.c:** - `g_tls_sticky_ss[TINY_NUM_CLASSES][TINY_STICKY_RING] = {0}` - `g_tls_sticky_idx[TINY_NUM_CLASSES][TINY_STICKY_RING] = {0}` - `g_tls_sticky_pos[TINY_NUM_CLASSES] = {0}` **効果:** ``` Before: 1T: 2.09M ✅ | 4T: SEGV 💀 After: 1T: 2.41M ✅ | 4T: 4.19M ✅ (+15% 1T, SEGV解消) ``` **テスト:** ```bash # 1 thread: 完走 ./larson_hakmem 2 8 128 1024 1 12345 1 → Throughput = 2,407,597 ops/s ✅ # 4 threads: 完走(以前は SEGV) ./larson_hakmem 2 8 128 1024 1 12345 4 → Throughput = 4,192,155 ops/s ✅ ``` **調査協力:** Task agent (ultrathink mode) による完璧な根本原因特定 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-07 01:27:04 +09:00
// ss_active_dec_one(ss); // REMOVED: Already called inside ss_remote_push()
if (was_empty) {
extern unsigned long long g_remote_free_transitions[];
g_remote_free_transitions[ss->size_class]++;
ss_partial_publish((int)ss->size_class, ss);
}
} else {
// Fallback: direct freelist push (legacy)
if (debug_guard) fprintf(stderr, "[FREE_SS] Using LEGACY freelist push (not remote queue)\n");
void* prev = meta->freelist;
*(void**)base = prev;
meta->freelist = base;
Phase 6-2.3~6-2.5: Critical bug fixes + SuperSlab optimization (WIP) ## Phase 6-2.3: Fix 4T Larson crash (active counter bug) ✅ **Problem:** 4T Larson crashed with "free(): invalid pointer", OOM errors **Root cause:** core/hakmem_tiny_refill_p0.inc.h:103 - P0 batch refill moved freelist blocks to TLS cache - Active counter NOT incremented → double-decrement on free - Counter underflows → SuperSlab appears full → OOM → crash **Fix:** Added ss_active_add(tls->ss, from_freelist); **Result:** 4T stable at 838K ops/s ✅ ## Phase 6-2.4: Fix SEGV in random_mixed/mid_large_mt benchmarks ✅ **Problem:** bench_random_mixed_hakmem, bench_mid_large_mt_hakmem → immediate SEGV **Root cause #1:** core/box/hak_free_api.inc.h:92-95 - "Guess loop" dereferenced unmapped memory when registry lookup failed **Root cause #2:** core/box/hak_free_api.inc.h:115 - Header magic check dereferenced unmapped memory **Fix:** 1. Removed dangerous guess loop (lines 92-95) 2. Added hak_is_memory_readable() check before dereferencing header (core/hakmem_internal.h:277-294 - uses mincore() syscall) **Result:** - random_mixed (2KB): SEGV → 2.22M ops/s ✅ - random_mixed (4KB): SEGV → 2.58M ops/s ✅ - Larson 4T: no regression (838K ops/s) ✅ ## Phase 6-2.5: Performance investigation + SuperSlab fix (WIP) ⚠️ **Problem:** Severe performance gaps (19-26x slower than system malloc) **Investigation:** Task agent identified root cause - hak_is_memory_readable() syscall overhead (100-300 cycles per free) - ALL frees hit unmapped_header_fallback path - SuperSlab lookup NEVER called - Why? g_use_superslab = 0 (disabled by diet mode) **Root cause:** core/hakmem_tiny_init.inc:104-105 - Diet mode (default ON) disables SuperSlab - SuperSlab defaults to 1 (hakmem_config.c:334) - BUT diet mode overrides it to 0 during init **Fix:** Separate SuperSlab from diet mode - SuperSlab: Performance-critical (fast alloc/free) - Diet mode: Memory efficiency (magazine capacity limits only) - Both are independent features, should not interfere **Status:** ⚠️ INCOMPLETE - New SEGV discovered after fix - SuperSlab lookup now works (confirmed via debug output) - But benchmark crashes (Exit 139) after ~20 lookups - Needs further investigation **Files modified:** - core/hakmem_tiny_init.inc:99-109 - Removed diet mode override - PERFORMANCE_INVESTIGATION_REPORT.md - Task agent analysis (303x instruction gap) **Next steps:** - Investigate new SEGV (likely SuperSlab free path bug) - OR: Revert Phase 6-2.5 changes if blocking progress 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-07 20:31:01 +09:00
tiny_failfast_log("free_local_legacy", ss->size_class, ss, meta, ptr, prev);
do {
static int g_mask_en = -1;
if (__builtin_expect(g_mask_en == -1, 0)) {
const char* e = getenv("HAKMEM_TINY_FREELIST_MASK");
g_mask_en = (e && *e && *e != '0') ? 1 : 0;
}
if (__builtin_expect(g_mask_en, 0) && prev == NULL) {
uint32_t bit = (1u << slab_idx);
atomic_fetch_or_explicit(&ss->freelist_mask, bit, memory_order_release);
}
} while (0);
meta->used--;
ss_active_dec_one(ss);
if (prev == NULL) {
ss_partial_publish((int)ss->size_class, ss);
}
}
// 空検出は別途(ホットパス除外)
}
}