Files
hakmem/core/box/tiny_front_hot_box.h

228 lines
9.0 KiB
C
Raw Normal View History

Phase 4-Step2: Add Hot/Cold Path Box (+7.3% performance) Implemented Hot/Cold Path separation using Box pattern for Tiny allocations: Performance Improvement (without PGO): - Baseline (Phase 26-A): 53.3 M ops/s - Hot/Cold Box (Phase 4-Step2): 57.2 M ops/s - Gain: +7.3% (+3.9 M ops/s) Implementation: 1. core/box/tiny_front_hot_box.h - Ultra-fast hot path (1 branch) - Removed range check (caller guarantees valid class_idx) - Inline cache hit path with branch prediction hints - Debug metrics with zero overhead in Release builds 2. core/box/tiny_front_cold_box.h - Slow cold path (noinline, cold) - Refill logic (batch allocation from SuperSlab) - Drain logic (batch free to SuperSlab) - Error reporting and diagnostics 3. core/front/malloc_tiny_fast.h - Updated to use Hot/Cold Boxes - Hot path: tiny_hot_alloc_fast() (1 branch: cache empty check) - Cold path: tiny_cold_refill_and_alloc() (noinline, cold attribute) - Clear separation improves i-cache locality Branch Analysis: - Baseline: 4-5 branches in hot path (range check + cache check + refill logic mixed) - Hot/Cold Box: 1 branch in hot path (cache empty check only) - Reduction: 3-4 branches eliminated from hot path Design Principles (Box Pattern): ✅ Single Responsibility: Hot path = cache hit only, Cold path = refill/errors ✅ Clear Contract: Hot returns NULL on miss, Cold handles miss ✅ Observable: Debug metrics (TINY_HOT_METRICS_*) gated by NDEBUG ✅ Safe: Branch prediction hints (TINY_HOT_LIKELY/UNLIKELY) ✅ Testable: Isolated hot/cold paths, easy A/B testing PGO Status: - Temporarily disabled (build issues with __gcov_merge_time_profile) - Will re-enable PGO in future commit after resolving gcc/lto issues - Current benchmarks are without PGO (fair A/B comparison) Other Changes: - .gitignore: Added *.d files (dependency files, auto-generated) - Makefile: PGO targets temporarily disabled (show informational message) - build_pgo.sh: Temporarily disabled (show "PGO paused" message) Next: Phase 4-Step3 (Front Config Box, target +5-8%) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-29 11:58:37 +09:00
// tiny_front_hot_box.h - Phase 4-Step2: Tiny Front Hot Path Box
// Purpose: Ultra-fast allocation path (5-7 branches max)
// Contract: TLS cache hit path only, falls back to cold path on miss
// Performance: Target +10-15% (60.6M → 68-75M ops/s)
//
// Design Principles (Box Pattern):
// 1. Single Responsibility: Hot path ONLY (cache hit)
// 2. Clear Contract: Assumes cache initialized, returns NULL on miss
// 3. Observable: Debug metrics (zero overhead in Release)
// 4. Safe: Pointer safety via branch hints, type-safe operations
// 5. Testable: Isolated from cold path, easy to benchmark
//
// Branch Count Analysis:
// Hot Path (cache hit):
// 1. class_idx range check (UNLIKELY)
// 2. cache empty check (LIKELY hit)
// 3. (header write - no branch)
// Total: 2 branches (down from 4-5)
//
// Cold Path (cache miss):
// Return NULL → caller handles via tiny_cold_refill_and_alloc()
#ifndef TINY_FRONT_HOT_BOX_H
#define TINY_FRONT_HOT_BOX_H
#include <stdint.h>
#include <stddef.h>
#include "../hakmem_build_flags.h"
#include "../hakmem_tiny_config.h"
#include "../tiny_region_id.h"
#include "../front/tiny_unified_cache.h" // For TinyUnifiedCache
#include "tiny_layout_box.h" // For tiny_user_offset()
Phase 4-Step2: Add Hot/Cold Path Box (+7.3% performance) Implemented Hot/Cold Path separation using Box pattern for Tiny allocations: Performance Improvement (without PGO): - Baseline (Phase 26-A): 53.3 M ops/s - Hot/Cold Box (Phase 4-Step2): 57.2 M ops/s - Gain: +7.3% (+3.9 M ops/s) Implementation: 1. core/box/tiny_front_hot_box.h - Ultra-fast hot path (1 branch) - Removed range check (caller guarantees valid class_idx) - Inline cache hit path with branch prediction hints - Debug metrics with zero overhead in Release builds 2. core/box/tiny_front_cold_box.h - Slow cold path (noinline, cold) - Refill logic (batch allocation from SuperSlab) - Drain logic (batch free to SuperSlab) - Error reporting and diagnostics 3. core/front/malloc_tiny_fast.h - Updated to use Hot/Cold Boxes - Hot path: tiny_hot_alloc_fast() (1 branch: cache empty check) - Cold path: tiny_cold_refill_and_alloc() (noinline, cold attribute) - Clear separation improves i-cache locality Branch Analysis: - Baseline: 4-5 branches in hot path (range check + cache check + refill logic mixed) - Hot/Cold Box: 1 branch in hot path (cache empty check only) - Reduction: 3-4 branches eliminated from hot path Design Principles (Box Pattern): ✅ Single Responsibility: Hot path = cache hit only, Cold path = refill/errors ✅ Clear Contract: Hot returns NULL on miss, Cold handles miss ✅ Observable: Debug metrics (TINY_HOT_METRICS_*) gated by NDEBUG ✅ Safe: Branch prediction hints (TINY_HOT_LIKELY/UNLIKELY) ✅ Testable: Isolated hot/cold paths, easy A/B testing PGO Status: - Temporarily disabled (build issues with __gcov_merge_time_profile) - Will re-enable PGO in future commit after resolving gcc/lto issues - Current benchmarks are without PGO (fair A/B comparison) Other Changes: - .gitignore: Added *.d files (dependency files, auto-generated) - Makefile: PGO targets temporarily disabled (show informational message) - build_pgo.sh: Temporarily disabled (show "PGO paused" message) Next: Phase 4-Step3 (Front Config Box, target +5-8%) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-29 11:58:37 +09:00
// ============================================================================
// Branch Prediction Macros (Pointer Safety - Prediction Hints)
// ============================================================================
// TINY_HOT_LIKELY: Hint compiler that condition is VERY likely true
// Usage: if (TINY_HOT_LIKELY(ptr != NULL)) { ... }
// Result: CPU pipeline optimized for hot path, cold path predicted as unlikely
#define TINY_HOT_LIKELY(x) __builtin_expect(!!(x), 1)
// TINY_HOT_UNLIKELY: Hint compiler that condition is VERY unlikely
// Usage: if (TINY_HOT_UNLIKELY(error)) { ... }
// Result: CPU pipeline avoids speculative execution of error path
#define TINY_HOT_UNLIKELY(x) __builtin_expect(!!(x), 0)
// ============================================================================
// Debug Metrics (Zero Overhead in Release)
// ============================================================================
#if !HAKMEM_BUILD_RELEASE
// Increment cache hit counter (debug only)
#define TINY_HOT_METRICS_HIT(class_idx) \
do { extern __thread uint64_t g_unified_cache_hit[]; \
g_unified_cache_hit[class_idx]++; } while(0)
// Increment cache miss counter (debug only)
#define TINY_HOT_METRICS_MISS(class_idx) \
do { extern __thread uint64_t g_unified_cache_miss[]; \
g_unified_cache_miss[class_idx]++; } while(0)
#else
// Release builds: macros expand to nothing (zero overhead)
#define TINY_HOT_METRICS_HIT(class_idx) ((void)0)
#define TINY_HOT_METRICS_MISS(class_idx) ((void)0)
#endif
// ============================================================================
// Box 2: Tiny Hot Alloc (Ultra-Fast Path)
// ============================================================================
// Ultra-fast allocation from TLS unified cache
//
// CONTRACT:
// Input: class_idx (0-7, caller must validate)
// Output: USER pointer (base+1) on success, NULL on miss
// Precondition: Cache initialized (caller ensures via lazy init or prewarm)
// Postcondition: Cache head advanced, object header written
//
// PERFORMANCE:
// Hot path (cache hit): 2 branches, 2-3 cache misses
// Cold path (cache miss): Returns NULL (caller handles)
//
// BRANCH ANALYSIS:
// 1. class_idx range check (UNLIKELY, safety)
// 2. cache empty check (LIKELY hit)
// 3. (no branch for header write, direct store)
//
// ASSEMBLY (expected, x86-64):
// mov g_unified_cache@TPOFF(%rax,%rdi,8), %rcx ; TLS cache access
// movzwl (%rcx), %edx ; head
// movzwl 2(%rcx), %esi ; tail
// cmp %dx, %si ; head != tail ?
// je .Lcache_miss
// mov 8(%rcx), %rax ; slots
// mov (%rax,%rdx,8), %rax ; base = slots[head]
// inc %dx ; head++
// and 6(%rcx), %dx ; head & mask
// mov %dx, (%rcx) ; store head
// movb $0xA0, (%rax) ; header magic
// or %dil, (%rax) ; header |= class_idx
// lea 1(%rax), %rax ; base+1 → USER
// ret
// .Lcache_miss:
// xor %eax, %eax ; return NULL
// ret
//
__attribute__((always_inline))
static inline void* tiny_hot_alloc_fast(int class_idx) {
extern __thread TinyUnifiedCache g_unified_cache[];
// TLS cache access (1 cache miss)
// NOTE: Range check removed - caller (hak_tiny_size_to_class) guarantees valid class_idx
TinyUnifiedCache* cache = &g_unified_cache[class_idx];
// Branch 1: Cache empty check (LIKELY hit)
// Hot path: cache has objects (head != tail)
// Cold path: cache empty (head == tail) → refill needed
if (TINY_HOT_LIKELY(cache->head != cache->tail)) {
// === HOT PATH: Cache hit (2-3 instructions) ===
// Pop from cache (1 cache miss for array access)
void* base = cache->slots[cache->head];
cache->head = (cache->head + 1) & cache->mask; // Fast modulo (power of 2)
// Debug metrics (zero overhead in release)
TINY_HOT_METRICS_HIT(class_idx);
// Write header + return USER pointer (no branch)
#if HAKMEM_TINY_HEADER_CLASSIDX
Phase 4-Step2: Add Hot/Cold Path Box (+7.3% performance) Implemented Hot/Cold Path separation using Box pattern for Tiny allocations: Performance Improvement (without PGO): - Baseline (Phase 26-A): 53.3 M ops/s - Hot/Cold Box (Phase 4-Step2): 57.2 M ops/s - Gain: +7.3% (+3.9 M ops/s) Implementation: 1. core/box/tiny_front_hot_box.h - Ultra-fast hot path (1 branch) - Removed range check (caller guarantees valid class_idx) - Inline cache hit path with branch prediction hints - Debug metrics with zero overhead in Release builds 2. core/box/tiny_front_cold_box.h - Slow cold path (noinline, cold) - Refill logic (batch allocation from SuperSlab) - Drain logic (batch free to SuperSlab) - Error reporting and diagnostics 3. core/front/malloc_tiny_fast.h - Updated to use Hot/Cold Boxes - Hot path: tiny_hot_alloc_fast() (1 branch: cache empty check) - Cold path: tiny_cold_refill_and_alloc() (noinline, cold attribute) - Clear separation improves i-cache locality Branch Analysis: - Baseline: 4-5 branches in hot path (range check + cache check + refill logic mixed) - Hot/Cold Box: 1 branch in hot path (cache empty check only) - Reduction: 3-4 branches eliminated from hot path Design Principles (Box Pattern): ✅ Single Responsibility: Hot path = cache hit only, Cold path = refill/errors ✅ Clear Contract: Hot returns NULL on miss, Cold handles miss ✅ Observable: Debug metrics (TINY_HOT_METRICS_*) gated by NDEBUG ✅ Safe: Branch prediction hints (TINY_HOT_LIKELY/UNLIKELY) ✅ Testable: Isolated hot/cold paths, easy A/B testing PGO Status: - Temporarily disabled (build issues with __gcov_merge_time_profile) - Will re-enable PGO in future commit after resolving gcc/lto issues - Current benchmarks are without PGO (fair A/B comparison) Other Changes: - .gitignore: Added *.d files (dependency files, auto-generated) - Makefile: PGO targets temporarily disabled (show informational message) - build_pgo.sh: Temporarily disabled (show "PGO paused" message) Next: Phase 4-Step3 (Front Config Box, target +5-8%) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-29 11:58:37 +09:00
tiny_region_id_write_header(base, class_idx); // 1-byte header at BASE
// Use centralized layout API for offset calculation
size_t user_offset = tiny_user_offset(class_idx);
return (void*)((char*)base + user_offset); // Return USER pointer
Phase 4-Step2: Add Hot/Cold Path Box (+7.3% performance) Implemented Hot/Cold Path separation using Box pattern for Tiny allocations: Performance Improvement (without PGO): - Baseline (Phase 26-A): 53.3 M ops/s - Hot/Cold Box (Phase 4-Step2): 57.2 M ops/s - Gain: +7.3% (+3.9 M ops/s) Implementation: 1. core/box/tiny_front_hot_box.h - Ultra-fast hot path (1 branch) - Removed range check (caller guarantees valid class_idx) - Inline cache hit path with branch prediction hints - Debug metrics with zero overhead in Release builds 2. core/box/tiny_front_cold_box.h - Slow cold path (noinline, cold) - Refill logic (batch allocation from SuperSlab) - Drain logic (batch free to SuperSlab) - Error reporting and diagnostics 3. core/front/malloc_tiny_fast.h - Updated to use Hot/Cold Boxes - Hot path: tiny_hot_alloc_fast() (1 branch: cache empty check) - Cold path: tiny_cold_refill_and_alloc() (noinline, cold attribute) - Clear separation improves i-cache locality Branch Analysis: - Baseline: 4-5 branches in hot path (range check + cache check + refill logic mixed) - Hot/Cold Box: 1 branch in hot path (cache empty check only) - Reduction: 3-4 branches eliminated from hot path Design Principles (Box Pattern): ✅ Single Responsibility: Hot path = cache hit only, Cold path = refill/errors ✅ Clear Contract: Hot returns NULL on miss, Cold handles miss ✅ Observable: Debug metrics (TINY_HOT_METRICS_*) gated by NDEBUG ✅ Safe: Branch prediction hints (TINY_HOT_LIKELY/UNLIKELY) ✅ Testable: Isolated hot/cold paths, easy A/B testing PGO Status: - Temporarily disabled (build issues with __gcov_merge_time_profile) - Will re-enable PGO in future commit after resolving gcc/lto issues - Current benchmarks are without PGO (fair A/B comparison) Other Changes: - .gitignore: Added *.d files (dependency files, auto-generated) - Makefile: PGO targets temporarily disabled (show informational message) - build_pgo.sh: Temporarily disabled (show "PGO paused" message) Next: Phase 4-Step3 (Front Config Box, target +5-8%) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-29 11:58:37 +09:00
#else
return base; // No-header mode: return BASE directly
#endif
}
// === COLD PATH: Cache miss ===
// Don't refill here - let caller handle via tiny_cold_refill_and_alloc()
// This keeps hot path small and predictable
TINY_HOT_METRICS_MISS(class_idx);
return NULL;
}
// ============================================================================
// Box 2b: Tiny Hot Free (Ultra-Fast Path)
// ============================================================================
// Ultra-fast free to TLS unified cache
//
// CONTRACT:
// Input: class_idx (0-7), base pointer (BASE, not USER)
// Output: 1=SUCCESS (pushed to cache), 0=FULL (caller handles)
// Precondition: Cache initialized, base is valid BASE pointer
// Postcondition: Cache tail advanced, object pushed to cache
//
// PERFORMANCE:
// Hot path (cache not full): 2 branches, 2-3 cache misses
// Cold path (cache full): Returns 0 (caller handles)
//
// BRANCH ANALYSIS:
// 1. class_idx range check (UNLIKELY, safety)
// 2. cache full check (UNLIKELY full)
//
__attribute__((always_inline))
static inline int tiny_hot_free_fast(int class_idx, void* base) {
extern __thread TinyUnifiedCache g_unified_cache[];
// TLS cache access (1 cache miss)
// NOTE: Range check removed - caller guarantees valid class_idx
TinyUnifiedCache* cache = &g_unified_cache[class_idx];
// Calculate next tail (for full check)
uint16_t next_tail = (cache->tail + 1) & cache->mask;
// Branch 1: Cache full check (UNLIKELY full)
// Hot path: cache has space (next_tail != head)
// Cold path: cache full (next_tail == head) → drain needed
if (TINY_HOT_LIKELY(next_tail != cache->head)) {
// === HOT PATH: Cache has space (2-3 instructions) ===
// Push to cache (1 cache miss for array write)
cache->slots[cache->tail] = base;
cache->tail = next_tail;
// Debug metrics (zero overhead in release)
#if !HAKMEM_BUILD_RELEASE
extern __thread uint64_t g_unified_cache_push[];
g_unified_cache_push[class_idx]++;
#endif
return 1; // SUCCESS
}
// === COLD PATH: Cache full ===
// Don't drain here - let caller handle via tiny_cold_drain_and_free()
#if !HAKMEM_BUILD_RELEASE
extern __thread uint64_t g_unified_cache_full[];
g_unified_cache_full[class_idx]++;
#endif
return 0; // FULL
}
// ============================================================================
// Performance Notes
// ============================================================================
// Expected improvements (Phase 4-Step2):
// - Random Mixed 256: 60.6M → 68-75M ops/s (+10-15%)
// - Tiny Hot 64B: Current → +10-15%
//
// Key optimizations:
// 1. Branch reduction: 4-5 → 2 branches (hot path)
// 2. Branch hints: LIKELY/UNLIKELY guide CPU pipeline
// 3. Hot/Cold separation: Keeps hot path small (better i-cache)
// 4. Always inline: Eliminates function call overhead
// 5. Metrics gated: Zero overhead in release builds
//
// Trade-offs:
// 1. Code size: +50-100 bytes per call site (inline expansion)
// 2. Cold path complexity: Caller must handle NULL/0 returns
// 3. Cache assumption: Assumes cache initialized (lazy init moved to caller)
#endif // TINY_FRONT_HOT_BOX_H