# Phase PERF-ULTRA-ALLOC-OPT-1 実装計画 **フェーズ名**: Phase PERF-ULTRA-ALLOC-OPT-1: C7 ULTRA alloc 内部最適化 **目的**: C7 ULTRA alloc(現在 7.66% self%)の hot path を直線化し、5-6% まで削減 **期待効果**: 全体 Mixed throughput で +2-3M ops/s(31.6M ops/s → 33-35M ops/s) --- ## 背景 ### Phase PERF-ULTRA-REBASE-1 の発見 2025-12-11 の perf 計測結果(C4-C7 ULTRA 全て ON, Mixed 16-1024B, 10M cycles): | 順位 | 関数 | self% | 判定 | |------|------|-------|------| | #1 | **C7 ULTRA alloc** | **7.66%** | ← **最大ボトルネック(新規発見)** | | #2 | C4-C7 ULTRA free群 | 5.41% | 次の候補 | | #3 | gate/front前段 | 2.51% | ✅ 既に十分薄い | | #4 | header関連 | < 0.17% | ✅ ULTRA で削減済み | ### 戦略転換 **これまで**: v4/v5/v6 などの新世代を追加 **今後**: 既に当たりが出ている **ULTRA 内部を細かく削る** 理由: - v6/v5 拡張は -12〜33% の大幅回帰(C5/C4 対応で失敗) - gate/front や header はもう改善の余地が少ない - C7 ULTRA alloc の 7.66% を 5-6% に削れば全体効果 2-3% --- ## 実装対象ファイル ### 主要ファイル - **core/box/tiny_c7_ultra_free_box.h** - TinyC7UltraFreeTLS 構造体定義 - tiny_c7_ultra_alloc_fast() / tiny_c7_ultra_alloc_cold() の宣言 - **core/box/tiny_c7_ultra_free_box.c** - tiny_c7_ultra_alloc_fast() 実装(メイン最適化対象) - TLS context の定義と初期化 - **core/front/malloc_tiny_fast.h** - tiny_c7_ultra_alloc() の呼び出し箇所 - alloc dispatcher からの pop ロジック ### 参照/確認ファイル - **core/box/tiny_c7_ultra_free_env_box.h** - tiny_c7_ultra_free_enabled() - ENV gate の状態確認 - **core/link_stubs.c** / **core/hakmem.c** - 起動時の ENV 初期化タイミング確認 --- ## 実装施策 ### 1. TLS ヒットパスの直線化 **確認項目**: ```c // tiny_c7_ultra_alloc_fast() のコード流 TinyC7UltraFreeTLS* ctx = tiny_c7_ultra_free_tls(); if (ctx->count > 0) { void* base = ctx->freelist[--ctx->count]; return tiny_base_to_user_inline(base); } ``` **チェック項目**: - [ ] `tiny_c7_ultra_free_enabled()` が hot path に含まれていないか - → lazy init 後は ENV 参照が不要。guard は不要。 - → if (!tiny_c7_ultra_free_enabled()) return NULL; のような early guard は削除 - [ ] `tiny_c7_ultra_free_tls()` が毎回呼ばれているか - → TLS pointer cache で 1 回だけ取得し、ローカル変数に格納 - → 複数回アクセスなら cache 再利用 - [ ] hot path に条件分岐が何個あるか(目標: 1-2個) - count > 0 チェックのみ - ユーザーポインタ計算は単純な演算のみ **実装例**: ```c // 最適化前の可能性が高い形 void* tiny_c7_ultra_alloc_fast(void) { if (!tiny_c7_ultra_free_enabled()) return NULL; TinyC7UltraFreeTLS* ctx = tiny_c7_ultra_free_tls(); if (ctx == NULL) return NULL; // ← 不要な null check if (ctx->count == 0) return NULL; ... } // 最適化後(expected) void* tiny_c7_ultra_alloc_fast(void) { // tiny_c7_ultra_free_enabled() check は呼び出し側で済ませる // (malloc_tiny_fast.h で if (tiny_c7_ultra_free_enabled()) { tiny_c7_ultra_alloc_fast(); } の形) TinyC7UltraFreeTLS* ctx = __thread_local_tls_c7; // 直接 TLS access if (likely(ctx->count > 0)) { void* base = ctx->freelist[--ctx->count]; return tiny_base_to_user_inline(base); } return NULL; } ``` ### 2. TLS freelist レイアウト最適化 **確認項目**: ```c // TinyC7UltraFreeTLS 構造体の定義確認 typedef struct TinyC7UltraFreeTLS { void* freelist[TINY_C7_ULTRA_FREE_CAP]; // 128 * 8B = 1024B uint8_t count; // + 1B = 1025B uintptr_t seg_base, seg_end; // + 16B = 1041B } TinyC7UltraFreeTLS; ``` **チェック項目**: - [ ] freelist と count が 1 cache line(64B)に収まるか - freelist[TINY_C7_ULTRA_FREE_CAP] サイズを確認(デフォルト 128 要素 = 1024B) - → 1024B は L1 キャッシュ(通常 32-64KB)を超過しているので、count のみが hot - → count アクセスは cache line 1 本で十分 - → freelist アクセスは cache misses が避けられない(alloc pop は 128 個のブロックを引き出すのに 1 個アクセスだから許容) - [ ] alloc hot data(count)が先頭に配置されているか - 現行は freq[capacity/count] が後ろ - → count を先頭に移動すれば、alloc pop 時の cache line access 1 本(count チェック)で済む - [ ] seg_base / seg_end は確実に slow path(free 時の segment learning)か - → alloc は seq_base を参照しない → hot path から外すべき **実装例**: ```c // 最適化後(期待値) typedef struct TinyC7UltraFreeTLS { // Hot path 用(64B以内想定) uint16_t count; // ← alloc pop で必須 uint8_t pad; void* freelist[128]; // ← large block, only used for pop // Cold path 用(segment learning など) uintptr_t seg_base, seg_end; // free 時のみ } TinyC7UltraFreeTLS; ``` ### 3. segment / page_meta アクセスの確認 **確認項目**: ```c // tiny_c7_ultra_free_fast() の segment learning ロジック確認 if (unlikely(ctx->seg_base == 0)) { SuperSlab* ss = ss_fast_lookup(base); ctx->seg_base = (uintptr_t)ss; ctx->seg_end = ctx->seg_base + (1u << ss->lg_size); } ``` **チェック項目**: - [ ] segment learning は cold path(unlikely)として外出しされているか - → 初回 free で 1 回だけ実行 - → 以降のすべての free は segment cached チェックで済む - → alloc は segment を参照しない - [ ] alloc hot path に page_meta access が混入していないか - → alloc は count チェックと base pop だけ - → page_meta は free 側のみで十分 - [ ] ss_fast_lookup() が cache miss 時のみか - → segment learning 時(unlikely)のみ - → これは現状で十分(1 回だけ) --- ## 計測戦略 ### Phase 0: ベースライン(実装前) ```bash # C7-only bench HAKMEM_TINY_C7_ULTRA_FREE_ENABLED=1 \ perf record -F 5000 --call-graph dwarf -e cycles:u \ ./bench_allocators_hakmem C7 1000000 400 1 # Mixed bench HAKMEM_TINY_C7_ULTRA_FREE_ENABLED=1 HAKMEM_FREE_PATH_STATS=1 \ perf record -F 5000 --call-graph dwarf -e cycles:u \ ./bench_random_mixed_hakmem 10000000 8192 1 # perf report で self% を記録 perf report --stdio | grep -A 5 "tiny_c7_ultra_alloc" ``` **期待値**: C7 ULTRA alloc の self% = **7.66%**(ベースライン) ### Phase 1: 各施策の実装と計測 実装後、同じ条件で 3 回計測して平均を取る: ```bash # 最適化後の計測(同じコマンド) for i in 1 2 3; do HAKMEM_TINY_C7_ULTRA_FREE_ENABLED=1 \ perf record -F 5000 --call-graph dwarf -e cycles:u \ ./bench_random_mixed_hakmem 10000000 8192 1 perf report --stdio > perf_opt_run$i.txt done ``` **目標**: - self% = 5-6%(削減幅 2-3%) - throughput = +2-3M ops/s(31.6M → 33-35M ops/s) ### Phase 2: 詳細分析 ```bash # 各関数の self% を比較表にまとめる # - tiny_c7_ultra_alloc_fast vs tiny_c7_ultra_alloc_cold # - ss_fast_lookup / segment learning の slow path confirmation # - freelist access パターン ``` --- ## 検証チェックリスト 実装完了時、以下をすべて確認: - [ ] コンパイル成功(warning なし) - [ ] リンク成功 - [ ] C7-only bench で SEGV/assert なし - [ ] Mixed bench で SEGV/assert なし - [ ] perf 計測で self% が 5-6% に達している - [ ] throughput が +2-3M ops/s 改善 - [ ] 各関数の分岐数が max 2-3(直線化目標) - [ ] TLS access が 1 回だけ(cache 再利用) --- ## 実装時の注意点 ### 1. ENV チェックの配置 ```c // ❌ Hot path 内で ENV check if (tiny_c7_ultra_free_enabled()) { // ← hot path で毎回? ... } // ✅ 呼び出し側(malloc_tiny_fast.h dispatcher)でチェック if (tiny_c7_ultra_free_enabled()) { void* p = tiny_c7_ultra_alloc_fast(); if (p) return p; } ``` ### 2. TLS cache 再利用 ```c // ❌ 複数回 TLS access TinyC7UltraFreeTLS* ctx = tiny_c7_ultra_free_tls(); if (ctx->count > 0) { void* base = ctx->freelist[--ctx->count]; // ← 1回目 ... } if (ctx->count > 0) { // ← 2回目の access ... } // ✅ ローカル変数に cache TinyC7UltraFreeTLS* ctx = tiny_c7_ultra_free_tls(); uint16_t count = ctx->count; if (likely(count > 0)) { void* base = ctx->freelist[--count]; ctx->count = count; return tiny_base_to_user_inline(base); } ``` ### 3. 構造体レイアウトの確認 ```c // 実装後、以下で hot field 配置を確認 // (gdb) p sizeof(TinyC7UltraFreeTLS) // (gdb) p &ctx->count, &ctx->freelist // → count が freelist より先頭にあることを確認 ``` --- ## 次フェーズへのつなぎ ### このフェーズの成功条件 - self% が 5-6% に低下 - throughput が +2-3M ops/s 達成 - 新しい bottleneck が浮上(page_of/segment 判定, so_alloc など) ### Phase 2 の候補(自動昇格) self% が 5-6% に達したら、次は **C4-C7 ULTRA free群(5.41%)** の軽量化を検討: - free side のTLS push パス直線化 - page_of / segment 判定との連携最適化 --- ## 参考資料 - **ホットパス分析**: `docs/analysis/TINY_CPU_HOTPATH_USERLAND_ANALYSIS.md` - **C7 ULTRA 設計**: `docs/analysis/TINY_C7_ULTRA_DESIGN.md` - **全体方針**: `CURRENT_TASK.md` 「Phase PERF-ULTRA-ALLOC-OPT-1 計画」