147 lines
6.1 KiB
Markdown
147 lines
6.1 KiB
Markdown
|
|
# 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()` を実行し直す必要がある
|
|||
|
|
|
|||
|
|
(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`(ヘッダ破壊)
|
|||
|
|
- After(Phase 13 v1):
|
|||
|
|
- C7: `next_off=1` → `memcpy(base+1, &next, 8)`(ヘッダ保持)
|
|||
|
|
|
|||
|
|
これにより 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` が保持される(ヘッダ破壊が起きない)
|
|||
|
|
|
|||
|
|
### 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 候補)
|