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:
@ -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 ✅
|
||||
|
||||
@ -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];
|
||||
|
||||
105
docs/analysis/ALLOC_TINY_FAST_DUALHOT_1_DESIGN.md
Normal file
105
docs/analysis/ALLOC_TINY_FAST_DUALHOT_1_DESIGN.md
Normal file
@ -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(保持して次の学びに使う)
|
||||
|
||||
Reference in New Issue
Block a user