Phase FREE-LEGACY-OPT-4-2/4-3: C6 ULTRA-free TLS cache + segment learning

Phase 4-2:
- Add TinyC6UltraFreeTLS structure with 128-slot TLS freelist
- Implement tiny_c6_ultra_free_fast/slow for C6 free hot path
- Add c6_ultra_free_fast counter to FreePathStats
- ENV gate: HAKMEM_TINY_C6_ULTRA_FREE_ENABLED (default: OFF)

Phase 4-3:
- Add segment learning on first C6 free via ss_fast_lookup()
- Learn seg_base/seg_end from SuperSlab for range check
- Increase cache capacity from 32 to 128 blocks

Results:
- Segment learning works: fast path captures blocks in segment
- However, without alloc integration, cache fills up and overflows to legacy
- Net effect: +1-3% (within noise range)
- Drain strategy also tested: no benefit (equal overhead)

Conclusion:
- Free-only TLS cache is limited without alloc-side integration
- Core v6 already has alloc/free integrated TLS (but -12% slower)
- Keep as research box (ENV default OFF)

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Moe Charm (CI)
2025-12-11 18:34:27 +09:00
parent 210633117a
commit 1b196b3ac0
8 changed files with 382 additions and 25 deletions

View File

@ -304,3 +304,220 @@ Phase FREE-LEGACY-OPT-4-2 で **C6 クラス** に ULTRA-Free lane を実装す
- **C5 (257-512B)**: Legacy の 25.8%(全体の 12.7%
- C5 も ULTRA-Free の候補だが、C6 の効果を確認してから判断すべき
### 次のアクションPhase 4-2
**最大ターゲット**: C6 Legacy freeLegacy の 51.4%、全体の 25.3%
**実装方針**:
1. C6_ULTRA_FREE_BOXfree-only, C6-onlyを追加
2. C7 ULTRA 風の TLS キャッシュ(`freelist[32]` + `count` + segment range check
3. ENV で opt-in`HAKMEM_TINY_C6_ULTRA_FREE_ENABLED=0`
4. alloc 側は既存ルートのまま(後続フェーズで検討)
**期待効果**:
- C6 Legacy を TLS キャッシュに逃がす → Legacy 半減
- Legacy: 49.2% → 24-27%
- Mixed: +5-8% 改善
## Phase FREE-LEGACY-OPT-4-2: C6_ULTRA_FREE_BOX 実装結果
### 実装内容
**コンポーネント**:
- `core/box/tiny_c6_ultra_free_env_box.h`: ENV ゲートHAKMEM_TINY_C6_ULTRA_FREE_ENABLED=0
- `core/box/tiny_c6_ultra_free_box.h/c`: C6 ULTRA-free TLS キャッシュ
- `core/front/malloc_tiny_fast.h`: free フロントへの接続C7 ULTRA 直後に分岐追加)
- `core/box/free_path_stats_box.h/c`: c6_ultra_free_fast カウンタ追加
**設計**:
- TLS に `freelist[32]` + `count` + `seg_base/seg_end` (segment range check)
- Fast path: seg_base が初期化済み && ptr in range && count < 32 TLS push
- Slow path: Legacy fallback
### A/B テスト結果Mixed 16-1024B
**ベースラインC6 ULTRA OFF**:
```
[FREE_PATH_STATS] total=542031 c7_ultra=275089 c6_ultra_free=0 small_v3=0 v6=0 tiny_v1=0 pool_v1=8081 remote=0 super_lookup=0 legacy_fb=266942
[FREE_PATH_STATS_LEGACY_BY_CLASS] c0=0 c1=0 c2=8746 c3=17279 c4=34727 c5=68871 c6=137319 c7=0
Throughput = 42.2M ops/s
```
**C6 ULTRA ON実測**:
```
[FREE_PATH_STATS] total=542031 c7_ultra=275089 c6_ultra_free=0 small_v3=0 v6=0 tiny_v1=0 pool_v1=8081 remote=0 super_lookup=0 legacy_fb=266942
[FREE_PATH_STATS_LEGACY_BY_CLASS] c0=0 c1=0 c2=8746 c3=17279 c4=34727 c5=68871 c6=137319 c7=0
Throughput = 39.9M ops/s (-5.5%)
```
### 問題分析
**観測された動作**:
- `c6_ultra_free=0` Fast path に全く入っていない
- `legacy_by_class[6]=137,319` C6 はすべて Legacy fallback のまま
- Throughput 低下 (42.2M 39.9M, -5.5%) ENV チェックのオーバーヘッドのみ
**根本原因**: **Segment 未初期化**
C6 ULTRA-free free-only設計のためTLS `seg_base/seg_end` を初期化する手段がない:
- Fast path の条件: `seg_base != 0 && ptr in range`
- しかしseg_base を設定する alloc 側の実装がない
- 結果: 常に seg_base == 0 slow pathLegacy fallbackに落ちる
**設計上の矛盾**:
1. **Free-only の限界**: TLS キャッシュを使うにはそのポインタが自分が管理する segment内かを確認する必要がある
2. **Segment 所有権**: C7 ULTRA alloc 時に segment を割り当てfree 時にその segment 内かを確認する
3. **C6 の現状**: alloc は既存ルートpool_v1を使いC6 ULTRA segment を持たない
4. **結論**: Segment range check は不可能 Fast path は機能しない
### 技術的洞察
**C7 ULTRA との違い**:
- C7 ULTRA alloc/free 両方を制御し専用 segment を持つ
- C6 ULTRA-free free のみを受けるためsegment 所有権がない
- 結果: Segment-based ownership check は不可能
**代替案の検討**:
1. **Option A: Segment 初期化を追加**
- C6 alloc 時に専用 segment を割り当て
- しかしこれはalloc は既存ルートのままという要件に反する
2. **Option B: Segment check を削除**
- すべての C6 ポインタを TLS pushownership check なし
- リスク: 他スレッドのポインタを TLS に入れる可能性cross-thread free 問題
3. **Option C: Header-based check**
- ポインタの header を読みC6 かを確認すでに class_idx == 6 で確認済み
- TLS ownership は諦めclass ベースの simple cache とする
4. **Option D: Phase 中止**
- Free-only での TLS キャッシュは設計上困難
- C6 の最適化は alloc/free 両方を含む v4/v5 に委ねる
### 推奨アクション
**短期**: Phase 4-2 研究箱実装のみ無効)」として保持
- ENV デフォルト OFF のまま
- ビルドエラーがないことを確認済み
- 実際の効果はなしseg_base == 0 のため
**中期**: C6 最適化は別アプローチで
- SmallHeap v4/v5/v6 などalloc/free 両方を制御する経路で C6 を最適化
- またはC6 専用の alloc/free 統合パスを新規実装
**長期**: Free-only 最適化の限界を認識
- TLS キャッシュは alloc/free 統合が前提
- Segment ownership なしでは安全な TLS push は困難
## Phase FREE-LEGACY-OPT-4-3: Segment 初回学習実装
### 目的
Phase 4-2 seg_base == 0 fast path に入らない問題を解決するため、**初回 C6 free `ss_fast_lookup` を呼び出しsegment 情報を学習**する
### 実装内容
**`core/box/tiny_c6_ultra_free_box.c` の修正**:
```c
void tiny_c6_ultra_free_fast(void* base, uint32_t class_idx) {
TinyC6UltraFreeTLS* ctx = &g_c6_ultra_free_tls;
// Phase 4-3: Learn segment on first C6 free
if (unlikely(ctx->seg_base == 0)) {
SuperSlab* ss = ss_fast_lookup(base);
if (ss != NULL) {
ctx->seg_base = (uintptr_t)ss;
ctx->seg_end = ctx->seg_base + (1u << ss->lg_size);
}
}
// Fast path: segment ownership check + TLS push
if (likely(ctx->seg_base != 0 &&
(uintptr_t)base >= ctx->seg_base &&
(uintptr_t)base < ctx->seg_end &&
ctx->count < TINY_C6_ULTRA_FREE_CAP)) {
ctx->freelist[ctx->count++] = base;
FREE_PATH_STAT_INC(c6_ultra_free_fast);
return;
}
// Slow path: fallback to legacy
tiny_c6_ultra_free_slow(base, class_idx);
}
```
**キャッシュサイズ拡大**:
- `TINY_C6_ULTRA_FREE_CAP = 128`32 128 に増加
### テスト結果
**C6 ULTRA ON + Segment 学習Mixed 16-1024B**:
```
[FREE_PATH_STATS] total=542031 c7_ultra=275089 c6_ultra_free=128 ...
[FREE_PATH_STATS_LEGACY_BY_CLASS] ... c6=137191 ...
```
**観測**:
- `c6_ultra_free=128` Fast path 128 ブロックがヒットseg_base 学習成功
- `legacy_by_class[6]=137,191` 残りの C6 (137K - 128 = ~137K) Legacy
### A/B 比較
| 測定 | OFF (M ops/s) | ON (M ops/s) | |
|------|---------------|--------------|-----|
| Run 1 | 40.2 | 41.5 | +3.2% |
| Run 2 | 42.5 | 42.9 | +1.1% |
| 平均 | 41.4 | 42.2 | +1-3% (誤差範囲) |
### 問題分析
**根本的限界**:
1. **キャッシュは満杯になる**: 128 ブロックがキャッシュされると以降の C6 free はすべて Legacy へオーバーフロー
2. **alloc との連携なし**: キャッシュされたブロックは alloc で再利用されない
3. **ドレイン戦略は効果なし**: ドレイン時に Legacy を呼ぶため結局同じオーバーヘッド
**数値で見る限界**:
- C6 total frees: 137,319
- Fast path hits: 128 (0.09%)
- Legacy fallback: 137,191 (99.91%)
### ドレイン戦略の試行(失敗)
満杯時に半分をドレインする戦略も試行:
```c
static void tiny_c6_ultra_free_drain_half(TinyC6UltraFreeTLS* ctx, uint32_t class_idx) {
uint8_t drain_count = ctx->count / 2;
for (uint8_t i = 0; i < drain_count; i++) {
hak_tiny_free_legacy_impl(ctx->freelist[i], class_idx);
}
// compact remaining...
}
```
**結果**:
- 137K C6 frees fast path を通過 (`c6_ultra_free=137319`)
- しかし137K blocks がドレインで Legacy に送られる
- **Throughput baseline と同等**+0%, 相殺
### 結論
**Free-only TLS キャッシュは alloc 連携なしでは効果限定的**
**既存の解決策**:
- Core v6 (`SmallHeapCtxV6`) に既に alloc/free 統合 TLS freelist が存在
- ただし Core v6 は現在 -12% baseline より遅い別途最適化が必要
**Phase 4-3 の成果**:
1. Segment 初回学習の実装`ss_fast_lookup` 活用
2. Fast path が機能することを確認
3. alloc 連携なしでは効果なし0.09% のブロックのみキャッシュ
4. ドレイン戦略も相殺で効果なし
**推奨**:
- C6 ULTRA Free **研究箱として維持**ENV デフォルト OFF
- 真の最適化には **alloc 側との TLS 連携**が必須
- Core v6 の改良または新規 alloc/free 統合パスの検討が必要