Phase 13 v1 + E5-2 retest: Both NEUTRAL, freeze as research boxes

Phase 13 v1: Header Write Elimination (C7 preserve header)
- Verdict: NEUTRAL (+0.78%)
- Implementation: HAKMEM_TINY_C7_PRESERVE_HEADER ENV gate (default OFF)
- Makes C7 nextptr offset conditional (0→1 when enabled)
- 4-point matrix A/B test results:
  * Case A (baseline): 51.49M ops/s
  * Case B (WRITE_ONCE=1): 52.07M ops/s (+1.13%)
  * Case C (C7_PRESERVE=1): 51.36M ops/s (-0.26%)
  * Case D (both): 51.89M ops/s (+0.78% NEUTRAL)
- Action: Freeze as research box (default OFF, manual opt-in)

Phase 5 E5-2: Header Write-Once retest (promotion test)
- Verdict: NEUTRAL (+0.54%)
- Motivation: Phase 13 Case B showed +1.13%, re-tested with dedicated 20-run
- Results (20-run):
  * Case A (baseline): 51.10M ops/s
  * Case B (WRITE_ONCE=1): 51.37M ops/s (+0.54%)
- Previous test: +0.45% (consistent with NEUTRAL)
- Action: Keep as research box (default OFF, manual opt-in)

Key findings:
- Header write tax optimization shows consistent NEUTRAL results
- Neither Phase 13 v1 nor E5-2 reaches GO threshold (+1.0%)
- Both implemented as reversible ENV gates for future research

Files changed:
- New: core/box/tiny_c7_preserve_header_env_box.{c,h}
- Modified: core/box/tiny_layout_box.h (C7 offset conditional)
- Modified: core/tiny_nextptr.h, core/box/tiny_header_box.h (comments)
- Modified: core/bench_profile.h (refresh sync)
- Modified: Makefile (add new .o files)
- Modified: scripts/run_mixed_10_cleanenv.sh (add C7_PRESERVE ENV)
- Docs: PHASE13_*, PHASE5_E5_2_HEADER_WRITE_ONCE_* (design/results)

Next: Phase 14 (Pointer-chase reduction, tcache-style intrusive LIFO)

🤖 Generated with Claude Code

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Moe Charm (CI)
2025-12-15 00:32:25 +09:00
parent 51f76153c4
commit cbb35ee27f
16 changed files with 836 additions and 27 deletions

View File

@ -10,6 +10,7 @@
#include "box/tiny_static_route_box.h" // tiny_static_route_refresh_from_env (Phase 3 C3)
#include "box/hakmem_env_snapshot_box.h" // hakmem_env_snapshot_refresh_from_env (Phase 4 E1)
#include "box/tiny_free_route_cache_env_box.h" // tiny_free_static_route_refresh_from_env (Phase 8)
#include "box/tiny_c7_preserve_header_env_box.h" // tiny_c7_preserve_header_env_refresh_from_env (Phase 13 v1)
#endif
// env が未設定のときだけ既定値を入れる
@ -184,5 +185,7 @@ static inline void bench_apply_profile(void) {
hakmem_env_snapshot_refresh_from_env();
// Phase 8: Sync free static route ENV cache after bench_profile putenv defaults.
tiny_free_static_route_refresh_from_env();
// Phase 13 v1: Sync C7 preserve header ENV cache after bench_profile putenv defaults.
tiny_c7_preserve_header_env_refresh_from_env();
#endif
}

View File

@ -0,0 +1,50 @@
// ============================================================================
// Phase 13 v1: Tiny C7 Preserve Header ENV Box (L0) - Implementation
// ============================================================================
#include "tiny_c7_preserve_header_env_box.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
// ============================================================================
// Global State
// ============================================================================
_Atomic int g_tiny_c7_preserve_header_enabled = -1;
// ============================================================================
// Init (Cold Path)
// ============================================================================
int tiny_c7_preserve_header_env_init(void) {
const char* env = getenv("HAKMEM_TINY_C7_PRESERVE_HEADER");
int enabled = 0; // default: OFF (opt-in)
if (env && (env[0] == '1' || strcmp(env, "true") == 0 || strcmp(env, "TRUE") == 0)) {
enabled = 1;
}
// Cache result
atomic_store_explicit(&g_tiny_c7_preserve_header_enabled, enabled, memory_order_relaxed);
// Log once (stderr for immediate visibility)
if (enabled) {
const char msg[] = "[C7_PRESERVE_HEADER] enabled\n";
ssize_t w = write(2, msg, sizeof(msg) - 1);
(void)w;
}
return enabled;
}
// ============================================================================
// Refresh (Cold Path, called from bench_profile)
// ============================================================================
void tiny_c7_preserve_header_env_refresh_from_env(void) {
// Reset to uninitialized state (-1)
// Next call to tiny_c7_preserve_header_enabled() will re-read ENV
atomic_store_explicit(&g_tiny_c7_preserve_header_enabled, -1, memory_order_relaxed);
}

View File

@ -0,0 +1,72 @@
// ============================================================================
// Phase 13 v1: Tiny C7 Preserve Header ENV Box (L0)
// ============================================================================
//
// Purpose: ENV gate for C7 header-preserving freelist layout
//
// Design: docs/analysis/PHASE13_HEADER_WRITE_ELIMINATION_1_DESIGN.md
// Instructions: docs/analysis/PHASE13_HEADER_WRITE_ELIMINATION_1_NEXT_INSTRUCTIONS.md
//
// Strategy:
// - C7 (1025-2048B) の freelist が header を壊さないようにする
// - nextptr offset を 0→1 に変更header 1B をスキップ)
// - これにより alloc 時の header 再書き込みを削減できる
//
// ENV:
// HAKMEM_TINY_C7_PRESERVE_HEADER=0/1 (default: 0, opt-in)
//
// API:
// tiny_c7_preserve_header_enabled() -> int
// tiny_c7_preserve_header_env_refresh_from_env()
//
// Box Theory:
// - L0: This file (ENV gate,戻せる)
// - L1: tiny_layout_box.h (SSOT: tiny_nextptr_offset)
// - L2: tiny_nextptr.h, tiny_header_box.h (affected code)
//
// Safety:
// - ENV-gated (default OFF, opt-in)
// - Reversible (ENV toggle)
// - Minimal change (C7 offset 0→1 のみ)
//
// ============================================================================
#ifndef TINY_C7_PRESERVE_HEADER_ENV_BOX_H
#define TINY_C7_PRESERVE_HEADER_ENV_BOX_H
#include <stdatomic.h>
// ============================================================================
// Global State (L0)
// ============================================================================
// Cached state: -1 (uninitialized), 0 (disabled), 1 (enabled)
extern _Atomic int g_tiny_c7_preserve_header_enabled;
// ============================================================================
// Hot Inline API (L0)
// ============================================================================
// Check if C7 preserve header is enabled
// Returns: 1 if enabled, 0 if disabled
static inline int tiny_c7_preserve_header_enabled(void) {
int val = atomic_load_explicit(&g_tiny_c7_preserve_header_enabled, memory_order_relaxed);
if (__builtin_expect(val == -1, 0)) {
// Lazy init: read ENV once
extern int tiny_c7_preserve_header_env_init(void);
val = tiny_c7_preserve_header_env_init();
}
return val;
}
// ============================================================================
// Cold API (L2)
// ============================================================================
// Refresh ENV cache (called from bench_profile after putenv)
// Pattern: Same as Phase 8 (FREE_STATIC_ROUTE)
extern void tiny_c7_preserve_header_env_refresh_from_env(void);
#endif // TINY_C7_PRESERVE_HEADER_ENV_BOX_H

View File

@ -41,13 +41,14 @@
//
// Returns:
// true - C1-C6: Header preserved at offset 0, next at offset 1
// false - C0, C7: Header overwritten by next pointer at offset 0
// false - C0: Header overwritten by next pointer at offset 0
// Phase 13 v1: C7 returns false (default) or true (HAKMEM_TINY_C7_PRESERVE_HEADER=1)
static inline bool tiny_class_preserves_header(int class_idx) {
#if HAKMEM_TINY_HEADER_CLASSIDX
// Delegate to tiny_layout_box.h specification (Single Source of Truth)
// next_off=0 → header overwritten (C0, C7)
// next_off=1 → header preserved (C1-C6)
// next_off=0 → header overwritten (C0, C7 default)
// next_off=1 → header preserved (C1-C6, C7 with HAKMEM_TINY_C7_PRESERVE_HEADER=1)
return tiny_nextptr_offset(class_idx) != 0;
#else
// Headers disabled globally
@ -87,7 +88,8 @@ static inline void tiny_header_write_if_preserved(void* base, int class_idx) {
// ============================================================================
//
// Validates header ONLY if this class preserves headers.
// For C0/C7, validation is impossible (next pointer is stored at offset 0).
// For C0, validation is impossible (next pointer is stored at offset 0).
// Phase 13 v1: C7 validation depends on HAKMEM_TINY_C7_PRESERVE_HEADER.
//
// Arguments:
// base - BASE pointer (not user pointer)

View File

@ -79,14 +79,29 @@ static inline size_t tiny_user_offset(int class_idx) {
// Offset for storing the freelist next pointer inside a freed block.
// This is distinct from tiny_user_offset():
// - User offset is always +1 in header mode.
// - Next offset is 0 for C0/C7 (cannot preserve header while free), else 1.
// - Next offset:
// - C0: always 0 (16B, cannot fit header+next)
// - C1-C6: always 1 (header-preserving)
// - C7: 0 (default) or 1 (Phase 13 v1: header-preserving)
static inline size_t tiny_nextptr_offset(int class_idx) {
#if HAKMEM_TINY_HEADERLESS
(void)class_idx;
return 0;
#elif HAKMEM_TINY_HEADER_CLASSIDX
// Bit pattern: C0=0, C1-C6=1, C7=0 → 0b01111110 = 0x7E
return (0x7Eu >> ((unsigned)class_idx & 7u)) & 1u;
// Phase 13 v1: C7 preserve header gate
// Bit pattern (default): C0=0, C1-C6=1, C7=0 → 0b01111110 = 0x7E
// Bit pattern (C7 preserve): C0=0, C1-C7=1 → 0b11111110 = 0xFE
unsigned int base_pattern = 0x7Eu; // default: C7 offset=0
// Phase 13 v1: Gate for C7 header-preserving layout
if (class_idx == 7) {
extern int tiny_c7_preserve_header_enabled(void);
if (tiny_c7_preserve_header_enabled()) {
base_pattern = 0xFEu; // C7 offset=1 (header-preserving)
}
}
return (base_pattern >> ((unsigned)class_idx & 7u)) & 1u;
#else
(void)class_idx;
return 0u;

View File

@ -1,7 +1,8 @@
// tiny_nextptr.h - Authoritative next-pointer offset/load/store for tiny boxes
//
// Finalized Phase E1-CORRECT spec (物理制約込み):
// P0.1 updated: C0 and C7 use offset 0, C1-C6 use offset 1 (header preserved)
// P0.1 updated: C0 uses offset 0, C1-C6 use offset 1 (header preserved)
// Phase 13 v1: C7 uses offset 0 (default) or 1 (HAKMEM_TINY_C7_PRESERVE_HEADER=1)
//
// HAKMEM_TINY_HEADER_CLASSIDX != 0 のとき:
//
@ -18,8 +19,8 @@
//
// Class 7:
// [1B header][payload 2047B]
// → headerは上書きし、next は base+0 に格納(最大サイズなので許容)
// → next_off = 0
// → next_off = 0 (default: headerは上書き)
// → next_off = 1 (Phase 13 v1: HAKMEM_TINY_C7_PRESERVE_HEADER=1)
//
// HAKMEM_TINY_HEADER_CLASSIDX == 0 のとき:
//
@ -56,7 +57,8 @@ static __thread void* g_tiny_next_ra1 __attribute__((unused)) = NULL;
static __thread void* g_tiny_next_ra2 __attribute__((unused)) = NULL;
// Compute freelist next-pointer offset within a block for the given class.
// P0.1 updated: C0 and C7 use offset 0, C1-C6 use offset 1 (header preserved)
// P0.1: C0 uses offset 0, C1-C6 use offset 1 (header preserved)
// Phase 13 v1: C7 uses offset 0 (default) or 1 (HAKMEM_TINY_C7_PRESERVE_HEADER=1)
// Rationale for C0: 8B stride cannot fit [1B header][8B next pointer] without overflow
static inline __attribute__((always_inline)) size_t tiny_next_off(int class_idx) {
return tiny_nextptr_offset(class_idx);
@ -186,7 +188,8 @@ static inline __attribute__((always_inline)) void* tiny_next_load(const void* ba
// - 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)
// P0.1: C0 uses offset 0 (overwrites header), C1-C6 use offset 1 (header preserved)
// Phase 13 v1: C7 uses offset 0 (default) or 1 (HAKMEM_TINY_C7_PRESERVE_HEADER=1)
static inline __attribute__((always_inline)) void tiny_next_store(void* base, int class_idx, void* next) {
size_t off = tiny_next_off(class_idx);