Phase ALLOC-TINY-FAST-DUALHOT-1: C0-C3 alloc direct path (WIP, -2% regression)

Add C0-C3 early-exit optimization to malloc_tiny_fast() similar to
FREE-TINY-FAST-DUALHOT-1. Skip policy snapshot for C0-C3 classes.

A/B Result (10-run, Mixed TINYV3_C7_SAFE):
- Baseline: 47.27M ops/s (median)
- Optimized: 46.10M ops/s (median)
- Result: -2.00% (regression, needs investigation)

ENV: HAKMEM_TINY_ALLOC_DUALHOT=0/1 (default OFF)

Implementation:
- core/front/malloc_tiny_fast.h: alloc_dualhot_enabled() + early-exit
- Design: docs/analysis/ALLOC_TINY_FAST_DUALHOT_1_DESIGN.md

Status: Research box (default OFF), needs root cause analysis

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Moe Charm (CI)
2025-12-13 04:28:52 +09:00
parent 2b567ac070
commit 0a7400d7d3
3 changed files with 167 additions and 4 deletions

View File

@ -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-1alloc の第2ホットを削る
DUALHOT optimized の perf で **alloc 側が次のボトルネック**に移行:
- `tiny_alloc_gate_fast` + `malloc` が合計 ~30%
- `free` は 2931% → 1617% に低下FREE-TINY-FAST-DUALHOT-1 の成果)
次の狙い:
- `malloc_tiny_fast()` でも **C0C3 を第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/BMixed / C6-heavy
---
## 前フェーズ: Phase POOL-MID-DN-BATCH 完了 ✅(研究箱として freeze 推奨)
--- ---
@ -8,15 +37,16 @@
**Summary**: **Summary**:
- **Goal**: Eliminate `mid_desc_lookup` from pool_free_v1 hot path by deferring inuse_dec - **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 - **Strategy**: TLS map batching (~32 pages/drain) + thread exit cleanup
- **Decision**: Default OFF (ENV gate),可用于生产环境测试 - **Decision**: Default OFF (ENV gate) のまま freezeopt-in 研究箱)
**Key Achievements**: **Key Achievements**:
- Hot path: Zero lookups (O(1) TLS map update only) - Hot path: Zero lookups (O(1) TLS map update only)
- Cold path: Batched lookup + atomic subtract (32x reduction in lookup frequency) - Cold path: Batched lookup + atomic subtract (32x reduction in lookup frequency)
- Thread-safe: pthread_key cleanup ensures pending ops drained on thread exit - 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**: **Deliverables**:
- `core/box/pool_mid_inuse_deferred_env_box.h` (ENV gate: HAKMEM_POOL_MID_INUSE_DEFERRED) - `core/box/pool_mid_inuse_deferred_env_box.h` (ENV gate: HAKMEM_POOL_MID_INUSE_DEFERRED)
@ -30,8 +60,13 @@
```bash ```bash
HAKMEM_POOL_MID_INUSE_DEFERRED=0 # Default (immediate dec) HAKMEM_POOL_MID_INUSE_DEFERRED=0 # Default (immediate dec)
HAKMEM_POOL_MID_INUSE_DEFERRED=1 # Enable deferred batching 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 ✅ ### Status: Phase MID-V35-HOTPATH-OPT-1 FROZEN ✅

View File

@ -129,6 +129,17 @@ static inline int front_gate_unified_enabled(void) {
// - USER pointer on success // - USER pointer on success
// - NULL on failure (caller falls back to normal path) // - 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)) __attribute__((always_inline))
static inline void* malloc_tiny_fast(size_t size) { static inline void* malloc_tiny_fast(size_t size) {
// Phase ALLOC-GATE-OPT-1: カウンタ散布 (1. 関数入口) // 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 // 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) // 2. Policy snapshot (TLS cached, single read)
const SmallPolicyV7* policy = small_policy_v7_snapshot(); const SmallPolicyV7* policy = small_policy_v7_snapshot();
SmallRouteKind route_kind = policy->route_kind[class_idx]; SmallRouteKind route_kind = policy->route_kind[class_idx];

View File

@ -0,0 +1,105 @@
# Phase ALLOC-TINY-FAST-DUALHOT-1 設計C0C3 を第2ホットとして扱う
## 背景(現状のボトルネック)
FREE-TINY-FAST-DUALHOT-1 により `free` の self% が大きく低下したため、Mixed の次のボトルネックが **alloc 側**に移った。
(例: `tiny_alloc_gate_fast` + `malloc` が合計 ~30% 付近)
ここで重要なのは、FREE 側の学びと同じ:
- C7 は ULTRA で “第1ホット”
- C0C3 は LEGACYUC/SFC/SLL 等)で “第2ホット”
- “第2ホット” を rare 扱いして `policy snapshot` を毎回踏むと損
---
## 目的
`malloc_tiny_fast()` の alloc 経路で、C0C3 については
- `small_policy_v7_snapshot()`policy snapshot
- `route_kind` 判定switch
を **スキップ**し、LEGACY の最短経路TLS unified cache hit → refillへ直行させる。
---
## 非目標(やらないこと)
- C4C7 のルーティング設計変更ULTRA/MID/v7 の構造は触らない)
- “PGO build 前提の大改造” はやらない(まずは小パッチで A/B
- 統計の常時 ONglobal 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 OFFA/B しやすくする)
### Fail-Fast / Safety
- alloc は cross-thread free と違い “所有者” 概念がないため、Larson ガード不要。
- ただし、将来 C0C3 を 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: C0C3 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/BMixed
- Baseline: `HAKMEM_TINY_ALLOC_DUALHOT=0`
- Opt: `HAKMEM_TINY_ALLOC_DUALHOT=1`
同一条件で 10-run。中央値と分散を比較。
### Gate 3: perfMixed
期待:
- `tiny_alloc_gate_fast` / `malloc` self% が下がる
- `main` が相対的に上がる(= allocator 側の余地を削った証拠)
---
## 成功条件
- Mixed: +2% 以上(または free DUALHOT と同等の “明確な改善”)
- C6-heavy: ±2% 以内(回帰なし)
- 回帰が出たら default OFF の研究箱として freeze保持して次の学びに使う