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>
6.1 KiB
Phase 13: Header Write Elimination v1(C7 Header-Preserving Freelist)
Date: 2025-12-14
Status: DESIGN(Phase 13 kickoff)→ ⚪ NEUTRAL (+0.78%)(research box freeze, default OFF)
0. Executive Summary(1枚)
Phase 12 の比較で system malloc (glibc) が hakmem より +63.7% 速いことが判明し、次の大きい構造差として “steady-state のヘッダ書き込み(write tax)” が最優先仮説になった。
ただし hakmem は free の hot path で HEADER_MAGIC を前提に ヘッダを読むため、ヘッダを “無くす/壊す” と安全性が崩れる。
そこで Phase 13 v1 は「ヘッダ自体は維持」しつつ、C7 の freelist でヘッダを上書きしない設計に寄せて、既存の E5-2 (Header write-once) を C7 にも適用可能にする。
狙い:
- C1-C6 は既に write-once で “alloc 時ヘッダ書き込み” をスキップ可能
- C7 は現状 “free の next がヘッダを潰す” ため、alloc で毎回ヘッダ再書き込みが必要
- C7 の next を **base+1(user 先頭)**へ移すとヘッダが保持され、write-once で alloc 側の再書き込みを削れる
1. 現状(なぜ C7 だけ毎回書いているのか)
1.1 重要な前提(現行の正)
- Free hot path(例:
core/front/malloc_tiny_fast.hのfree_tiny_fast())は、ptr-1のHEADER_MAGICを検証し- class_idx を header から抽出している
→ ヘッダの正しさは safety と fast path の前提
1.2 E5-2 (Header write-once) の適用範囲
core/box/tiny_header_box.hのtiny_header_finalize_alloc()が、HAKMEM_TINY_HEADER_WRITE_ONCE=1かつtiny_class_preserves_header(class_idx)=true(C1-C6) のとき、alloc 時のtiny_region_id_write_header()をスキップする。
1.3 C7 が write-once にならない理由(根本)
core/box/tiny_layout_box.hのtiny_nextptr_offset()が- C7 は
next_off=0(=base+0に next を書く)
→ free 時に ヘッダ領域を next pointer で上書きする
→ alloc で必ずtiny_region_id_write_header()を実行し直す必要がある
- C7 は
(C0 も同じだが、C0 は stride 8B のため base+1 に 8B next を置けない制約がある)
2. 提案(Phase 13 v1)
2.1 変更のコア
C7 の next pointer を base+1(user 先頭)に移す:
- Before(現行):
- C7:
next_off=0→*(void**)base = next(ヘッダ破壊)
- C7:
- After(Phase 13 v1):
- C7:
next_off=1→memcpy(base+1, &next, 8)(ヘッダ保持)
- C7:
これにより C7 が “header-preserving class” になり、E5-2 の write-once が C7 にも効く。
2.2 Box Theory(箱割り)
L0: tiny_c7_preserve_header_env_box (ENV gate, A/B, refresh)
↓
L1: tiny_layout_box (tiny_nextptr_offset の SSOT)
↓
L2: tiny_nextptr (next load/store は SSOT を参照)
↓
L3: tiny_header_box (class_preserves_header → write-once 適用)
境界は 1 箇所:
- 「C7 の next オフセット決定」=
tiny_nextptr_offset()に集約(他で分岐しない)
2.3 戻せる(A/B)
- ENV:
HAKMEM_TINY_C7_PRESERVE_HEADER=0/1(default: 0) - まずは research box として導入し、GO なら preset 昇格
3. Safety / Invariants(Fail-Fast)
3.1 不変条件
tiny_next_store/loadは 常にtiny_nextptr_offset()を参照(直書き禁止)tiny_class_preserves_header(class_idx)は offset!=0 で決まる(ハードコード禁止)- C7 preserve ON のとき:
- free 後も
*(uint8_t*)base == HEADER_MAGIC|clsが保持される(ヘッダ破壊が起きない)
- free 後も
3.2 Fail-Fast(debug 限定)
- デバッグのみ、C7 preserve ON のときに:
tiny_header_validate(base, 7, ...)の mismatch をワンショットで出す
- release では常時ログ無し、必要なら stats カウンタのみ
4. A/B 計測計画(同一バイナリ)
この変更は “freelist next の配置” を変えるため、本来は layout 差になるが、Phase 13 v1 は ENV で切替できるようにして同一バイナリ A/B を維持する(Phase 5-7 の教訓)。
4.1 4点マトリクス(必須)
| Case | HAKMEM_TINY_C7_PRESERVE_HEADER | HAKMEM_TINY_HEADER_WRITE_ONCE | 意味 |
|---|---|---|---|
| A | 0 | 0 | 現行 baseline |
| B | 0 | 1 | E5-2 のみ(C1-C6) |
| C | 1 | 0 | C7 next を user に移す(ヘッダは毎回書く) |
| D | 1 | 1 | Phase 13 v1 本命(C1-C7 を write-once) |
4.2 GO/NO-GO(Mixed 10-run)
- GO: mean +1.0% 以上
- NO-GO: mean -1.0% 以下
- NEUTRAL: ±1.0% → freeze(research box)
5. リスクと対策
リスク 1: C7 next が unaligned になり memcpy 経由で遅くなる
- 対策: Case C(write-once 無し)を必ず測り、layout 変更単体のコストを分離する
- もし C が大きく負ける場合:
- “C7 next offset=8(aligned)” の派生案を検討(Phase 13 v1b)
リスク 2: class_idx ハードコードが残っていて壊れる
- 対策:
rg "== 7|!= 7|C7 uses offset 0"を掃除し、SSOT(tiny_layout_box)参照に寄せる
リスク 3: ENV refresh が bench_profile putenv に追従しない
- 対策: Phase 8 と同様に
*_env_refresh_from_env()を用意し、bench_profile.hから呼ぶ
6. 次(Phase 13 以降の視界)
Phase 13 v1 は「ヘッダを “消す”」ではなく「steady-state のヘッダ再書き込みを減らす」に寄せる。
もし system malloc との差がまだ大きい場合、次の大テーマは:
- Thread cache(tcache 相当の構造)を TinyUnifiedCache に移植する(Phase 14 候補)