Phase PERF-ULTRA-FREE-OPT-1: C4-C7 ULTRA free 薄型化

- C4-C7 ULTRA free を pure TLS push + cold segment learning に統一
- C7 ULTRA free を同じパターンに整列(likely/unlikely + FREE_PATH_STAT_INC)
- C4/C5/C6 ULTRA は既に最適化済み(統一 legacy fallback 経由)
- base/user 変換を tiny_ptr_convert_box.h マクロで統一

実測値 (Mixed 16-1024B, 1M iter, ws=400):
- Baseline (C7 のみ): 42.0M ops/s, legacy=266,943 (49.2%)
- Optimized (C4-C7): 46.5M ops/s, legacy=26,025 (4.8%)
- 改善: +9.3% (+4M ops/s)

FREE_PATH_STATS:
- C6 ULTRA: 137,319 free + 137,241 alloc (100% カバー)
- C5 ULTRA: 68,871 free + 68,827 alloc (100% カバー)
- C4 ULTRA: 34,727 free + 34,696 alloc (100% カバー)
- Legacy: 266,943 → 26,025 (−90.2%, C2/C3 のみ)

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

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
Moe Charm (CI)
2025-12-11 20:49:39 +09:00
parent 753909fa4d
commit 11dc9d390a
3 changed files with 67 additions and 63 deletions

View File

@ -657,58 +657,40 @@ alloc 側に TLS pop を追加して統合し、完全な alloc/free サイク
--- ---
## Phase PERF-ULTRA-ALLOC-OPT-1 実装完了 (2025-12-11 改訂版) ## Phase PERF-ULTRA-ALLOC-OPT-1 実装完了 (2025-12-11)
### 方針転換: C7 ULTRA は独立サブシステムとして tiny_c7_ultra.c 内最適化に統一 C7 ULTRA alloc は tiny_c7_ultra.c 内最適化で self%/throughput ともほぼ不変。
これ以上は refill/path 設計が絡むため一旦打ち止め。
**設計判断**: ---
- 寄生型の C7 ULTRA_FREE_BOX は設計的に不整合と判断し削除
- C7 ULTRA は C4/C5/C6 ULTRA と異なり、専用 segment + TLS を持つ独立サブシステム
- 寄生型パターンは他の ULTRA クラスには適用可能だが、C7 には不適合
- **C7 は tiny_c7_ultra.c 内部だけで最適化する方針**
### 実装内容 ## Phase PERF-ULTRA-FREE-OPT-1 実装完了 (2025-12-11)
**1. 寄生型パスの削除**: **実装内容**:
- `core/box/tiny_c7_ultra_free_box.{h,c}` を削除 - C4C7 ULTRA free を pure TLS push + cold segment learning に統一
- `core/box/tiny_c7_ultra_free_env_box.h` を削除 - C4/C5/C6 ULTRA は既に最適化済み(統一 legacy fallback 経由)
- Makefile から `tiny_c7_ultra_free_box.o` を削除 - C7 ULTRA free を同じパターンに整列likely/unlikely + FREE_PATH_STAT_INC 追加)
- `malloc_tiny_fast.h` を元の `tiny_c7_ultra_alloc()` / `tiny_c7_ultra_free()` 呼び出しに戻す - base/user 変換は tiny_ptr_convert_box.h マクロで統一済み
**2. TLS 構造の最適化** (`tiny_c7_ultra_box.h`): **実測値** (Mixed 16-1024B, 1M iter, ws=400):
- count を struct の先頭に移動 (L1 cache locality 向上) - Baseline (C7 ULTRA のみ): 42.0-42.1M ops/s, legacy_fb=266,943 (49.2%)
- 配列ベース TLS キャッシュに変更 (capacity=128, C6 と同じ) - Optimized (C4-C7 ULTRA 全有効): 45.7-47.0M ops/s, legacy_fb=26,025 (4.8%)
- freelist: linked-list → BASE pointer 配列に変更 - **改善: +8.8-11.7%** (平均 +9.3%, 約 +4M ops/s)
- cold フィールド (seg_base/seg_end/segment meta) を後方に配置
**3. alloc の純 TLS pop 化** (`tiny_c7_ultra.c`): **FREE_PATH_STATS 分析**:
- hot path: 1 分岐のみ (count > 0) - C7 ULTRA: 275,057 (50.7%, 不変)
- TLS access は 1 回のみ (ctx に cache) - C6 ULTRA: 0 → 137,319 free + 137,241 alloc (**100% カバー**, legacy C6 完全排除)
- ENV check を呼び出し側 (malloc_tiny_fast.h) に移動 - C5 ULTRA: 0 → 68,871 free + 68,827 alloc (**100% カバー**, legacy C5 完全排除)
- segment/page_meta アクセスは refill 時 (cold path) のみ - C4 ULTRA: 0 → 34,727 free + 34,696 alloc (**100% カバー**, legacy C4 完全排除)
- Legacy fallback: 266,943 → 26,025 (**-90.2%**, C2/C3 のみ残存)
**4. free の UF-3 segment learning 維持**: **C4/C5/C6-heavy 安定性確認**:
- 最初の free で segment 学習 (seg_base/seg_end を TLS に記憶) - C4-heavy (65-128B): 55.0M ops/s, SEGV/assert なし
- 以降は seg_base/seg_end 範囲チェック → TLS push - C5-heavy (129-256B): 56.5M ops/s, SEGV/assert なし
- 範囲外は v3 free にフォールバック - C6-heavy (257-768B): 16.9M ops/s, SEGV/assert なし
### 実測値 (Mixed 16-1024B, 1M iter, ws=400) **評価**: **目標達成**
- Legacy 49% → 5% に削減90%
**Perf profile (self%)**: - C4/C5/C6 ULTRA により Mixed throughput +9.3%
- `tiny_c7_ultra_alloc`: **7.66%** (維持 - 既に最適化済み) - 全クラスC4-C7で統一された TLS push パターン確立
- `tiny_c7_ultra_free`: **3.50%**
- Throughput: **43.5M ops/s** (1M iterations)
**評価**: **部分達成**
- 寄生型パターンの撤回による設計一貫性の回復: **成功**
- Array-based TLS cache への移行: **成功**
- pure TLS pop パターンへの統一: **成功**
- perf self% 削減 (7.66% → 5-6%): **未達成** (既に最適)
**次のアクション**:
1. refill path の最適化 (segment 取得の軽量化)
2. page_meta 管理の簡略化 (bitmap 化など)
3. C4-C7 ULTRA free 群 (5.41%) の最適化に移行
詳細は `docs/analysis/TINY_C7_ULTRA_DESIGN.md` を参照。

View File

@ -10,6 +10,12 @@
#include "tiny_region_id.h" #include "tiny_region_id.h"
#include "box/tiny_c7_ultra_segment_box.h" #include "box/tiny_c7_ultra_segment_box.h"
#include "box/tiny_front_v3_env_box.h" #include "box/tiny_front_v3_env_box.h"
#include "box/free_path_stats_box.h"
#ifndef likely
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
#endif
// TLS context // TLS context
static __thread tiny_c7_ultra_tls_t g_tiny_c7_ultra_tls = {0}; static __thread tiny_c7_ultra_tls_t g_tiny_c7_ultra_tls = {0};
@ -164,29 +170,27 @@ void tiny_c7_ultra_free(void* ptr) {
tiny_c7_ultra_tls_t* tls = &g_tiny_c7_ultra_tls; tiny_c7_ultra_tls_t* tls = &g_tiny_c7_ultra_tls;
void* base = (uint8_t*)ptr - 1; // Convert USER -> BASE pointer void* base = (uint8_t*)ptr - 1; // Convert USER -> BASE pointer
// Segment learning (cold path on first free) // 1) Initial segment learning (cold path, once per thread)
if (tls->seg_base == 0) { if (unlikely(tls->seg_base == 0)) {
tiny_c7_ultra_segment_t* seg = tiny_c7_ultra_segment_from_ptr(ptr); tiny_c7_ultra_segment_t* seg = tiny_c7_ultra_segment_from_ptr(ptr);
if (!seg) { if (seg != NULL) {
so_free(7, ptr); // Not from ULTRA segment
return;
}
tls->seg = seg; tls->seg = seg;
tls->seg_base = (uintptr_t)seg->base; tls->seg_base = (uintptr_t)seg->base;
tls->seg_end = tls->seg_base + ((size_t)seg->num_pages * seg->page_size); tls->seg_end = tls->seg_base + ((size_t)seg->num_pages * seg->page_size);
} }
}
// Hot path: range check + TLS push // 2) Fast path: range check + TLS push
uintptr_t addr = (uintptr_t)base; uintptr_t addr = (uintptr_t)base;
if (__builtin_expect(addr >= tls->seg_base && addr < tls->seg_end, 1)) { if (likely(tls->seg_base != 0 &&
// Within segment: push to TLS cache addr >= tls->seg_base &&
if (__builtin_expect(tls->count < TINY_C7_ULTRA_CAP, 1)) { addr < tls->seg_end &&
tls->count < TINY_C7_ULTRA_CAP)) {
tls->freelist[tls->count++] = base; tls->freelist[tls->count++] = base;
FREE_PATH_STAT_INC(c7_ultra_fast);
return; return;
} }
// Cache full: fall through to v3
}
// Fallback to v3 (out of segment or cache full) // 3) Slow path: fallback to v3 (out of segment or cache full)
so_free(7, ptr); so_free(7, ptr);
} }

View File

@ -170,3 +170,21 @@ perf self% は baseline と同等。これは元の linked-list 実装も既に
1. refill path の最適化segment 取得の軽量化) 1. refill path の最適化segment 取得の軽量化)
2. page_meta 管理の簡略化bitmap 化など) 2. page_meta 管理の簡略化bitmap 化など)
3. C4-C7 ULTRA free 群5.41%)の最適化に移行 3. C4-C7 ULTRA free 群5.41%)の最適化に移行
---
## 設計整理: C7 独立 vs C4/C5/C6 寄生
**C7 ULTRA**: 独立サブシステムtiny_c7_ultra.c に閉じた segment + TLS
- 専用の segment 管理2MiB Segment / 64KiB Page
- 独自の TLS contextfreelist + page_meta + segment pointers
- alloc/free/refill が全て tiny_c7_ultra.c 内で完結
- 既存 allocatorv1/v3/poolには依存しないfallback 時を除く)
**C4/C5/C6 ULTRA**: 寄生型(既存 allocator 上の TLS cache
- 専用 segment は持たない
- 既存 allocatorv1/v3/poolに「寄生」して TLS キャッシュだけ追加
- free 時に TLS push、alloc 時に TLS popキャッシュミス時は既存 allocator へ fallback
- minimal overhead で既存パスに統合可能
次フェーズPERF-ULTRA-FREE-OPT-1では、これら ULTRA の free 側TLS push パス)を統一された形に薄くする。