diff --git a/CURRENT_TASK.md b/CURRENT_TASK.md index 9e5a11e9..5ae0b9b4 100644 --- a/CURRENT_TASK.md +++ b/CURRENT_TASK.md @@ -1,6 +1,35 @@ # 本線タスク(現在) -## 現在地: Phase POOL-MID-DN-BATCH 完了 ✅ → 次フェーズ選定待ち +## 現在地: Phase FREE-TINY-FAST-DUALHOT-1 完了 ✅ (+9.51% improvement) + +- **Latest**: Phase FREE-TINY-FAST-DUALHOT-1 completed (2025-12-13) +- **Improvement**: +9.51% throughput (44.50M → 48.74M ops/s, 10-run mean, MIXED_TINYV3_C7_SAFE) +- **Strategy**: Recognize C0-C3 (48% of frees) as "second hot path", not cold + - Skip policy snapshot + route determination + - Direct inline to `tiny_legacy_fallback_free_base()` for C0-C3 + - Safety gate: `HAKMEM_TINY_LARSON_FIX=1` disables optimization +- **Design**: `docs/analysis/FREE_TINY_FAST_DUALHOT_1_DESIGN.md` +- **Implementation**: `core/front/malloc_tiny_fast.h` (lines 433-449) +- **Commit**: `2b567ac07` - Phase FREE-TINY-FAST-DUALHOT-1 + +## Next Phase: Phase ALLOC-TINY-FAST-DUALHOT-1(alloc の第2ホットを削る) + +DUALHOT optimized の perf で **alloc 側が次のボトルネック**に移行: +- `tiny_alloc_gate_fast` + `malloc` が合計 ~30% +- `free` は 29–31% → 16–17% に低下(FREE-TINY-FAST-DUALHOT-1 の成果) + +次の狙い: +- `malloc_tiny_fast()` でも **C0–C3 を第2ホット**として扱い、`small_policy_v7_snapshot()` をスキップして LEGACY 最短へ直行。 +- 設計: `docs/analysis/ALLOC_TINY_FAST_DUALHOT_1_DESIGN.md` + +実装指示(小パッチ): +1) ENV gate `HAKMEM_TINY_ALLOC_DUALHOT=0/1`(default OFF) +2) `core/front/malloc_tiny_fast.h` の `malloc_tiny_fast()` に `class_idx<=3` early-exit を追加 +3) health + 10-run A/B(Mixed / C6-heavy) + +--- + +## 前フェーズ: Phase POOL-MID-DN-BATCH 完了 ✅(研究箱として freeze 推奨) --- @@ -8,15 +37,16 @@ **Summary**: - **Goal**: Eliminate `mid_desc_lookup` from pool_free_v1 hot path by deferring inuse_dec -- **Mixed (bench_mid_large_mt_hakmem)**: **+2.8%** improvement ✅ (7.94M → 8.16M ops/s, median) +- **Performance**: 当初の計測では改善が見えたが、後続解析で「stats の global atomic」が大きな外乱要因だと判明 + - Stats OFF + Hash map の再計測では **概ねニュートラル(-1〜-2%程度)** - **Strategy**: TLS map batching (~32 pages/drain) + thread exit cleanup -- **Decision**: Default OFF (ENV gate),可用于生产环境测试 +- **Decision**: Default OFF (ENV gate) のまま freeze(opt-in 研究箱) **Key Achievements**: - Hot path: Zero lookups (O(1) TLS map update only) - Cold path: Batched lookup + atomic subtract (32x reduction in lookup frequency) - Thread-safe: pthread_key cleanup ensures pending ops drained on thread exit -- Stats: 82K deferred hits, 2.5K drain calls, 3.5K empty transitions +- Stats: `HAKMEM_POOL_MID_INUSE_DEFERRED_STATS=1` のときのみ有効(default OFF) **Deliverables**: - `core/box/pool_mid_inuse_deferred_env_box.h` (ENV gate: HAKMEM_POOL_MID_INUSE_DEFERRED) @@ -30,8 +60,13 @@ ```bash HAKMEM_POOL_MID_INUSE_DEFERRED=0 # Default (immediate dec) HAKMEM_POOL_MID_INUSE_DEFERRED=1 # Enable deferred batching +HAKMEM_POOL_MID_INUSE_MAP_KIND=linear|hash # Default: linear +HAKMEM_POOL_MID_INUSE_DEFERRED_STATS=0/1 # Default: 0 (keep OFF for perf) ``` +**Health smoke**: +- OFF/ON の最小スモークは `scripts/verify_health_profiles.sh` で実行 + --- ### Status: Phase MID-V35-HOTPATH-OPT-1 FROZEN ✅ diff --git a/core/front/malloc_tiny_fast.h b/core/front/malloc_tiny_fast.h index b3c0deae..a2d70653 100644 --- a/core/front/malloc_tiny_fast.h +++ b/core/front/malloc_tiny_fast.h @@ -129,6 +129,17 @@ static inline int front_gate_unified_enabled(void) { // - USER pointer on success // - NULL on failure (caller falls back to normal path) // + +// Phase ALLOC-TINY-FAST-DUALHOT-1: C0-C3 early-exit gate (default OFF) +static inline int alloc_dualhot_enabled(void) { + static int g = -1; + if (__builtin_expect(g == -1, 0)) { + const char* e = getenv("HAKMEM_TINY_ALLOC_DUALHOT"); + g = (e && *e && *e != '0') ? 1 : 0; + } + return g; +} + __attribute__((always_inline)) static inline void* malloc_tiny_fast(size_t size) { // Phase ALLOC-GATE-OPT-1: カウンタ散布 (1. 関数入口) @@ -155,6 +166,18 @@ static inline void* malloc_tiny_fast(size_t size) { // C7 ULTRA miss → fall through to policy-based routing } + // Phase ALLOC-TINY-FAST-DUALHOT-1: C0-C3 direct path (second hot path) + // Skip expensive policy snapshot and route determination for C0-C3. + // Measurements show C0-C3 is 48% of allocations, not rare. + if (__builtin_expect(alloc_dualhot_enabled() && class_idx <= 3, 0)) { + // Direct to LEGACY unified cache (no policy snapshot) + void* ptr = tiny_hot_alloc_fast(class_idx); + if (TINY_HOT_LIKELY(ptr != NULL)) { + return ptr; + } + return tiny_cold_refill_and_alloc(class_idx); + } + // 2. Policy snapshot (TLS cached, single read) const SmallPolicyV7* policy = small_policy_v7_snapshot(); SmallRouteKind route_kind = policy->route_kind[class_idx]; diff --git a/docs/analysis/ALLOC_TINY_FAST_DUALHOT_1_DESIGN.md b/docs/analysis/ALLOC_TINY_FAST_DUALHOT_1_DESIGN.md new file mode 100644 index 00000000..72e1300a --- /dev/null +++ b/docs/analysis/ALLOC_TINY_FAST_DUALHOT_1_DESIGN.md @@ -0,0 +1,105 @@ +# Phase ALLOC-TINY-FAST-DUALHOT-1 設計(C0–C3 を第2ホットとして扱う) + +## 背景(現状のボトルネック) + +FREE-TINY-FAST-DUALHOT-1 により `free` の self% が大きく低下したため、Mixed の次のボトルネックが **alloc 側**に移った。 + +(例: `tiny_alloc_gate_fast` + `malloc` が合計 ~30% 付近) + +ここで重要なのは、FREE 側の学びと同じ: + +- C7 は ULTRA で “第1ホット” +- C0–C3 は LEGACY(UC/SFC/SLL 等)で “第2ホット” +- “第2ホット” を rare 扱いして `policy snapshot` を毎回踏むと損 + +--- + +## 目的 + +`malloc_tiny_fast()` の alloc 経路で、C0–C3 については + +- `small_policy_v7_snapshot()`(policy snapshot) +- `route_kind` 判定(switch) + +を **スキップ**し、LEGACY の最短経路(TLS unified cache hit → refill)へ直行させる。 + +--- + +## 非目標(やらないこと) + +- C4–C7 のルーティング設計変更(ULTRA/MID/v7 の構造は触らない) +- “PGO build 前提の大改造” はやらない(まずは小パッチで A/B) +- 統計の常時 ON(global atomic)を入れない(外乱要因になる) + +--- + +## 設計(Box Theory) + +### 箱と境界 + +- 対象ホット関数: `core/front/malloc_tiny_fast.h` の `malloc_tiny_fast(size)` +- 境界: `class_idx` 確定直後(size→class の 1 回目)に “第2ホット” を判定して early-exit +- 既存の Box は変更しない(`tiny_hot_alloc_fast()` と `tiny_cold_refill_and_alloc()` を再利用) + +### 戻せる(A/B) + +- ENV gate を 1 つ追加する: + - `HAKMEM_TINY_ALLOC_DUALHOT=0/1` + - 初期は default OFF(A/B しやすくする) + +### Fail-Fast / Safety + +- alloc は cross-thread free と違い “所有者” 概念がないため、Larson ガード不要。 +- ただし、将来 C0–C3 を v6/v7 等へ載せる実験に備え、ENV でいつでも OFF に戻せるようにする。 + +--- + +## 具体案(擬似コード) + +差し込み位置は `class_idx` を確定し、C7 ULTRA early-exit を試した **直後**。 + +```c +// C7 ULTRA early-exit (既存) +if (class_idx == 7 && tiny_c7_ultra_enabled_env()) { ... } + +// NEW: C0–C3 DUALHOT +if (alloc_dualhot_enabled() && class_idx <= 3) { + void* p = tiny_hot_alloc_fast(class_idx); + if (p) return p; + return tiny_cold_refill_and_alloc(class_idx); +} + +// 既存: policy snapshot + route_kind switch +const SmallPolicyV7* policy = small_policy_v7_snapshot(); +switch (policy->route_kind[class_idx]) { ... } +``` + +--- + +## 計測手順(GO/NO-GO) + +### Gate 1: 健康診断 + +`scripts/verify_health_profiles.sh` を default 設定(ENV OFF)で 1 回。 + +### Gate 2: A/B(Mixed) + +- Baseline: `HAKMEM_TINY_ALLOC_DUALHOT=0` +- Opt: `HAKMEM_TINY_ALLOC_DUALHOT=1` + +同一条件で 10-run。中央値と分散を比較。 + +### Gate 3: perf(Mixed) + +期待: +- `tiny_alloc_gate_fast` / `malloc` self% が下がる +- `main` が相対的に上がる(= allocator 側の余地を削った証拠) + +--- + +## 成功条件 + +- Mixed: +2% 以上(または free DUALHOT と同等の “明確な改善”) +- C6-heavy: ±2% 以内(回帰なし) +- 回帰が出たら default OFF の研究箱として freeze(保持して次の学びに使う) +